R4g+R5 (mesh-controller): the controller asks no build, build-agent and the merge gate go, register by reference (design 47) #448
@@ -120,6 +120,8 @@ type DeclaredSeat struct {
|
||||
// Serves are the verbs the holder answers, request and reply.
|
||||
Serves []string
|
||||
RetainSeconds int
|
||||
// PulledByKind is Seat's: a worker per accepted kind, derived from the holders' seat traffic, not here.
|
||||
PulledByKind bool
|
||||
}
|
||||
|
||||
// ConsumerFor is the durable consumer a module's declarations imply, or false when it subscribes
|
||||
@@ -258,7 +260,9 @@ func ConsumersOf(users []Principal) []ModuleConsumer {
|
||||
// redelivery: nothing is delivered that nobody asked for. A long build keeps its own ask alive
|
||||
// (stillWorking); the ack wait is for a holder that died.
|
||||
func HolderConsumerFor(node, module string, seat DeclaredSeat) (Consumer, bool) {
|
||||
if len(seat.Accepts) == 0 {
|
||||
if len(seat.Accepts) == 0 || seat.PulledByKind {
|
||||
// A seat pulled by kind has a worker per kind, which its holders' seat traffic derives (SeatTrafficObjects): one
|
||||
// worker filtered on every accept would overlap them on the work queue.
|
||||
return Consumer{}, false
|
||||
}
|
||||
return Consumer{
|
||||
|
||||
@@ -92,6 +92,9 @@ type Seat struct {
|
||||
// DeclaredBy is the module that declares the seat. On a kinded bench it alone submits work to a kind
|
||||
// and answers its proofs (novox/hq ADR 0259 §8): the router, not any user or watcher of the bench.
|
||||
DeclaredBy string
|
||||
// PulledByKind says each accepted kind is a worker of its own on the seat's queue, filtered to that kind, which
|
||||
// every holder pulls in the order Accepts lists them: the build seats' (novox/hq ADR 0525 §2, ADR 0536 §4).
|
||||
PulledByKind bool
|
||||
}
|
||||
|
||||
// A Principal is one user of the bus. Its permissions are derived from what it declares and
|
||||
|
||||
@@ -61,7 +61,14 @@ func SeatTrafficOf(module string, holds, uses, watches []Seat) SeatTraffic {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(s.Accepts) > 0 {
|
||||
if len(s.Accepts) > 0 && s.PulledByKind {
|
||||
// One worker per accepted kind, which every holder pulls in the order of Accepts (ADR 0525 §4, ADR 0536 §4):
|
||||
// `SEAT_<SEAT>_<KIND>_worker`, filtered to `accept.<kind>` (mesh-sdk delivery.AskWorker).
|
||||
for _, a := range s.Accepts {
|
||||
t.Workers = append(t.Workers, Worker{Stream: seatStreamName(s.Name), Consumer: WorkerName(s.Name, a),
|
||||
Filter: "mesh.seat." + s.Name + ".accept." + a})
|
||||
}
|
||||
} else if len(s.Accepts) > 0 {
|
||||
stream := seatStreamName(s.Name)
|
||||
filter := "mesh.seat." + s.Name + ".accept.>"
|
||||
if kind != "" {
|
||||
@@ -156,7 +163,7 @@ func seatTrafficGrants(t SeatTraffic) (pub, sub []string) {
|
||||
// isNewTraffic says whether a seat carries any of the rules above, so a seat that carries none is
|
||||
// composed exactly as before them.
|
||||
func (s Seat) isNewTraffic() bool {
|
||||
return s.Kinded || len(s.ByCaller) > 0 || len(s.Proofs) > 0 || len(s.Records) > 0
|
||||
return s.Kinded || len(s.ByCaller) > 0 || len(s.Proofs) > 0 || len(s.Records) > 0 || s.PulledByKind
|
||||
}
|
||||
|
||||
// plainVerbs is a seat's accepts or emits with those the rules above compose taken out: a verb named by
|
||||
|
||||
@@ -388,3 +388,47 @@ func TestVerifiedSenderNeedsTheChannelsAndTheRoutersMachinesRootFree(t *testing.
|
||||
t.Error("no router placed, and verified-sender kept")
|
||||
}
|
||||
}
|
||||
|
||||
func buildSeat() Seat {
|
||||
return Seat{Name: "node-build-go", Scope: "node", Accepts: []string{"delivery", "check", "recheck"},
|
||||
Serves: []string{"current", "pause", "resume"}, PulledByKind: true}
|
||||
}
|
||||
|
||||
// 1887/A6, ADR 0525 §2: the builder, which uses the build seat, may put an ask on each kind; a session's account may
|
||||
// not; the seat's holder pulls a worker per kind and no catch-all worker is made beside them.
|
||||
func TestTheBuilderMayAskABuildSeatAndASessionMayNot(t *testing.T) {
|
||||
builder := perms(t, Principal{Kind: KindModule, Node: "novox", Module: "mesh-delivery-builder", Uses: []Seat{buildSeat()}})
|
||||
for _, kind := range []string{"delivery", "check", "recheck"} {
|
||||
if s := "mesh.seat.node-build-go.accept." + kind; !allowed(builder.Publish, s) {
|
||||
t.Errorf("the builder may not publish %s", s)
|
||||
}
|
||||
}
|
||||
session := perms(t, Principal{Kind: KindPerson, Module: "jochen", Invokes: []string{"mesh-controller.status"}})
|
||||
if allowed(session.Publish, "mesh.seat.node-build-go.accept.delivery") {
|
||||
t.Error("a session's account may put an ask")
|
||||
}
|
||||
holds := []Seat{buildSeat()}
|
||||
traffic := SeatTrafficOf("mesh-build-go", holds, nil, nil)
|
||||
var workers []string
|
||||
for _, w := range traffic.Workers {
|
||||
workers = append(workers, w.Consumer+" "+w.Filter)
|
||||
}
|
||||
// Sorted: the order a holder pulls them in is its own (mesh-sdk delivery.AskKinds).
|
||||
want := "SEAT_NODE_BUILD_GO_CHECK_worker mesh.seat.node-build-go.accept.check," +
|
||||
"SEAT_NODE_BUILD_GO_DELIVERY_worker mesh.seat.node-build-go.accept.delivery," +
|
||||
"SEAT_NODE_BUILD_GO_RECHECK_worker mesh.seat.node-build-go.accept.recheck"
|
||||
if strings.Join(workers, ",") != want {
|
||||
t.Errorf("workers = %v, want one per kind", workers)
|
||||
}
|
||||
pub, _ := seatTrafficGrants(traffic)
|
||||
if !allowed(pub, "$JS.API.CONSUMER.MSG.NEXT.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_CHECK_worker") {
|
||||
t.Error("the holder may not pull the check worker")
|
||||
}
|
||||
_, consumers := SeatTrafficObjects([]Principal{{Kind: KindModule, Module: "mesh-build-go", Holds: holds}})
|
||||
if len(consumers) != 3 {
|
||||
t.Errorf("consumers = %+v, want the three kinds' workers", consumers)
|
||||
}
|
||||
if _, made := HolderConsumerFor("shanks", "mesh-build-go", DeclaredSeat{Name: "node-build-go", Accepts: buildSeat().Accepts, PulledByKind: true}); made {
|
||||
t.Error("a catch-all worker would overlap the kinds' workers on the work queue")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +356,11 @@ func asSeat(s catalogue.SeatDeclaration, declarer string) broker.Seat {
|
||||
seat := broker.Seat{Name: s.Name, Scope: s.Scope, Accepts: s.Accepts, Emits: s.Emits,
|
||||
Serves: catalogue.VerbNames(s.Serves), Kinded: s.Kinded, ByCaller: s.ByCaller, Proofs: s.Proofs,
|
||||
DeclaredBy: declarer}
|
||||
// A build seat's kinds are each a worker of its own, pulled in their order (novox/hq ADR 0525 §2; issue 1887 A6): the
|
||||
// mesh's definition of the role, compiled, never stored.
|
||||
if own, ok := catalogue.SeatNamed(s.Name); ok && own.PulledByKind {
|
||||
seat.PulledByKind = true
|
||||
}
|
||||
// A seat's records are its declaring module's buckets, named as the bus holds them (ADR 0259 §3).
|
||||
for _, r := range s.Records {
|
||||
if declarer != "" {
|
||||
@@ -371,7 +376,7 @@ func MeshSeats() []broker.DeclaredSeat {
|
||||
var out []broker.DeclaredSeat
|
||||
for _, s := range catalogue.SeatsWithAProtocol() {
|
||||
out = append(out, broker.DeclaredSeat{
|
||||
Name: s.Name, Accepts: s.Accepts, Emits: s.Emits, Serves: catalogue.VerbNames(s.Serves),
|
||||
Name: s.Name, Accepts: s.Accepts, Emits: s.Emits, Serves: catalogue.VerbNames(s.Serves), PulledByKind: s.PulledByKind,
|
||||
})
|
||||
}
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user