Give a work-queue stream its one consumer, drop-only dead letters (issue 1178)

This commit is contained in:
jochen
2026-10-19 02:33:22 +02:00
parent 97ee33e8f4
commit de651e0fd1
16 changed files with 467 additions and 22 deletions
+34
View File
@@ -187,6 +187,9 @@ func declaredFor(m catalogue.Manifest, seats map[string]catalogue.SeatDeclaratio
Logs: logsOf(m),
// And the streams it owns (novox/hq ADR 0363).
Streams: streamsOf(m),
// And its own work-queue streams, each with the one process its bus.queues names as the
// consumer (novox/hq ADR 0436 §1, §2, §6).
Queues: queueOwnersOf(m),
// And the tools its health asks (novox/hq ADR 0240): the machine's node-engine is granted them.
Checks: catalogue.HealthChecks(m),
// And whether it runs as an account of its own (novox/hq ADR 0259 §8).
@@ -428,6 +431,37 @@ func streamsOf(m catalogue.Manifest) []broker.ModuleStream {
return out
}
// queueOwnersOf is every work-queue stream of a module's own that one of its processes names in bus.queues,
// with that process's id and the stream's declared max-deliveries (novox/hq ADR 0436 §1, §2, §6). In
// resource order, so composition is repeatable; a queue naming a stream the module does not own, or one two
// processes name, is caught at registration (catalogue.WorkQueueStreamProblems) and never reaches here.
func queueOwnersOf(m catalogue.Manifest) []broker.QueueOwner {
buses := m.ProcessBuses()
if len(buses) == 0 {
return nil
}
byName := map[string]catalogue.StreamDeclaration{}
for _, s := range m.Streams {
byName[s.Name] = s
}
var out []broker.QueueOwner
for _, r := range m.Resources {
id := fmt.Sprint(r["id"])
b, declared := buses[id]
if !declared {
continue
}
for _, q := range b.Queues {
s, known := byName[q]
if !known || s.Retention != catalogue.StreamRetentionWorkQueue {
continue
}
out = append(out, broker.QueueOwner{Process: id, Stream: s.BusName(), MaxDeliver: s.MaxDeliveries})
}
}
return out
}
// DeclaredStreams is every module stream the catalogue declares, registered modules assigned or not: a
// stream exists from registration, as a log does (novox/hq ADR 0363 §1).
func (i *Inventory) DeclaredStreams(ctx context.Context) ([]broker.ModuleStream, error) {
+22
View File
@@ -65,3 +65,25 @@ func TestANonWorkQueueStreamIsCarriedAsNotAWorkQueue(t *testing.T) {
t.Fatalf("the plain stream: %+v", got)
}
}
// 1178/AC1: the process a work-queue stream's bus.queues names is read from the manifest, by its resource
// id, with the stream's own max-deliveries — what the broker composes the one durable consumer from.
func TestTheQueueOwnersOfAWorkQueueStreamAreReadFromItsProcess(t *testing.T) {
sender := catalogue.Manifest{Module: "mesh-mail", Emits: []string{"mail.send.sent", "mail.send.failed"},
Streams: []catalogue.StreamDeclaration{{Name: "mail-sends", Events: []string{"mail.send.approved"},
Retention: catalogue.StreamRetentionWorkQueue, MaxDeliveries: 5}},
Resources: []map[string]any{
{"id": "account", "type": "user", "name": "mesh-mail-send"},
{"id": "send", "type": "process", "run": []any{"./mesh-mail", "send"}, "user": "mesh-mail-send",
"bus": map[string]any{"emits": []any{"mail.send.sent", "mail.send.failed"},
"queues": []any{"mail-sends"}}},
}}
got := queueOwnersOf(sender)
if len(got) != 1 || got[0].Process != "send" || got[0].Stream != "MAIL_SENDS" || got[0].MaxDeliver != 5 {
t.Fatalf("the sender's queue owners: %+v", got)
}
// And a module with no process declaring bus has none.
if got := queueOwnersOf(catalogue.Manifest{Module: "feed"}); got != nil {
t.Fatalf("a module with no process bus has queue owners: %+v", got)
}
}