The consume side on NATS, and the window held by the server
The other implementation behind the seam, so the store-window guarantee now has both: one loop, one message at a time, the same window deciding. What differs is where a held message lives, and that is the whole point of the move — the AMQP side keeps an unacknowledged delivery in this process, bounded by the prefetch and lost if the controller stops; this keeps eight bytes saying when the window opened, and the message stays the server's. Checked against a running server, seven claims that reasoning cannot answer: a report is heard and leaves the work queue; one the store cannot take is naked with a delay, stays in the stream, and is recorded when the store returns; one about a superseded declaration is settled without being acted on; one the store never takes is let go once the bound passes; a heartbeat is heard and nothing is persisted; and the enrolment answer reaches the address the request carried in its payload — the test design 25 §2 asks for, so the reason for that field cannot quietly become folklore. Three things the wiring forced into the open: **The controller could not have consumed a module event.** Its permissions granted no event subject to subscribe and no ack subject on the events stream, so every announcement would have been redelivered for ever, refused by the list it already had. Both narrow: each followed subject named, not `mesh.mod.*.>`. **The controller's consumers are not derived.** It files no manifest, so its authority cannot come from a declaration that does not exist; they sit beside the mesh's own streams and are asserted the same way. No max-deliver on CONTROL — the window's bound is the controller's, and a server that dead-lettered first would discard the push the stream exists to protect. **Channels, not callbacks.** The library would run a handler on its own goroutine, and the window's bookkeeping is unlocked because the AMQP loop never had two.
This commit is contained in:
@@ -92,6 +92,34 @@ type OverNATS struct {
|
||||
JS nats.JetStreamContext
|
||||
}
|
||||
|
||||
// The subjects a node publishes on, and the controller listens to.
|
||||
//
|
||||
// One tree, and each name says who it is about: `mesh.control.<node>.…` is a node's own, which is
|
||||
// what lets a node's account be granted exactly its own prefix and nothing of any other node's
|
||||
// (design 25 §2, §4). The two that belong to no node — an enrolment, because a machine enrolling
|
||||
// has no name the mesh has agreed to yet, and a build's outcome, because a builder is not
|
||||
// reporting about itself — are named directly.
|
||||
const (
|
||||
// EnrolSubject is where a joining machine asks. Its enrolment user may publish here and
|
||||
// nowhere else, so a leaked token buys nothing but the chance to enrol.
|
||||
EnrolSubject = "mesh.control.enrol"
|
||||
|
||||
// BuiltSubject is where a build's outcome lands, for results nobody was waiting for.
|
||||
BuiltSubject = "mesh.control.built"
|
||||
|
||||
// AliveSubjects is every node's heartbeat. Core NATS, never a stream: a lost heartbeat is the
|
||||
// next heartbeat, and a stream of them is the mesh's least valuable message competing for
|
||||
// retention with its most valuable (design 25 §3).
|
||||
AliveSubjects = "mesh.control.*.alive"
|
||||
)
|
||||
|
||||
// ReportSubject is where one node says what it did. On the CONTROL stream, because it is the
|
||||
// message the store-window guarantee is about (ADR 0083).
|
||||
func ReportSubject(node string) string { return "mesh.control." + node + ".report" }
|
||||
|
||||
// AliveSubject is one node's heartbeat.
|
||||
func AliveSubject(node string) string { return "mesh.control." + node + ".alive" }
|
||||
|
||||
// EventSubject is where a module's event lands. Derived from the emitter, never taken from the
|
||||
// caller: a source that could differ from the subject is an envelope that can lie about its
|
||||
// origin, and on NATS the account's permissions make the subject the authority (design 29 §2).
|
||||
|
||||
Reference in New Issue
Block a user