From 8e81d8761092e95e4fc5ed47eea31a49cd2595ff Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 27 Oct 2026 02:04:01 +0100 Subject: [PATCH] Let a replicated mesh seat be held on several nodes in CheckPlacements, so the seed of today's placement is not refused (issue 1126) --- go/delivery/placement.go | 8 +++++++- go/delivery/placement_test.go | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/go/delivery/placement.go b/go/delivery/placement.go index 99ed5de..0c97a12 100644 --- a/go/delivery/placement.go +++ b/go/delivery/placement.go @@ -371,7 +371,8 @@ func (p Placement) Held() []catalogue.Held { // CheckPlacements is the catalogue's own check of every placement file (ADR 0549 ยง1), over the placements by node, the // modules the catalogue holds and the nodes enrolled. It returns every refusal, in node order, never only the first: a // placement naming a node not enrolled or a module the catalogue does not hold; two modules of one node on the same host -// port; a pin, binding or holding naming a provider that is not placed where it says; a seat of scope mesh held twice; +// port; a pin, binding or holding naming a provider that is not placed where it says; a seat of scope mesh held twice, unless the seat is replicated (catalogue.Seat's Replicated, ADR 0223), which +// is held once per node; // two nodes with one address on the private network; more than one hub. // A host port a manifest declares without a number and a requirement left unbound are the composition's to find (its // ForeseenPorts and Unbound), which the catalogue's composition check reads. @@ -442,6 +443,11 @@ func CheckPlacements(placements map[string]Placement, held func(module string) b continue } seat := h.Claim + "/" + h.Kind + if s, known := catalogue.SeatNamed(h.Claim); known && s.Replicated { + // A replicated mesh seat (ADR 0223) is held on several nodes at once, each answering the same thing; + // it is still held once on each node. + seat += "/" + node + } if other, twice := meshSeats[seat]; twice { refusals = append(refusals, fmt.Sprintf("the seat %s, held once for the mesh, is held on %s and %s", h.Claim, other, node)) } diff --git a/go/delivery/placement_test.go b/go/delivery/placement_test.go index 3993556..2290dda 100644 --- a/go/delivery/placement_test.go +++ b/go/delivery/placement_test.go @@ -148,6 +148,16 @@ func TestPlacement(t *testing.T) { if r := CheckPlacements(map[string]Placement{"g14": g14, "novox": novox}, held, enrolled); len(r) != 0 { t.Fatalf("a sound placement is refused: %v", r) } + // A replicated mesh seat (ADR 0223) is held on two nodes; a seat that is not replicated is refused there. + for seat, refused := range map[string]bool{"mesh-dns-resolver": false, "mesh-broker": true} { + a := Placement{Node: "ace", Modules: []PlacedModule{{Module: "dns"}}, Holds: []Holding{{Claim: seat, Scope: "mesh", Module: "dns"}}} + b := a + b.Node = "novox" + r := CheckPlacements(map[string]Placement{"ace": a, "novox": b}, held, enrolled) + if (len(r) != 0) != refused { + t.Errorf("%s on two nodes: refusals %v, want refused %v", seat, r, refused) + } + } clash := g14 clash.Modules = append([]PlacedModule{{Module: "ghost", Ports: map[string]int{"80": 18080}}}, g14.Modules...) clash.Modules[2] = PlacedModule{Module: "app", Ports: map[string]int{"8080": 18080}, Bindings: map[string]Provider{"postgres": {Node: "ace", Module: "postgres"}}}