diff --git a/go/buscompose/modstreams.go b/go/buscompose/modstreams.go index 8505473e..55fab00a 100644 --- a/go/buscompose/modstreams.go +++ b/go/buscompose/modstreams.go @@ -3,6 +3,7 @@ package buscompose import ( "fmt" "sort" + "strings" ) // moduleStreamPrefix starts every subject a module's own stream takes. @@ -14,6 +15,19 @@ func ModuleStreamSubject(module, event string) string { return moduleStreamPrefix + module + ".event." + event } +// WirePattern is a stream's local event pattern as the bus spells it: `**` is `>`, `*` stays `*`. A reader turns a +// stream's events into the bus's subjects with it (design 46 7-approval-mark §5): the grants part composing a stream, and +// the tool runner deriving a restricted stream's marked subjects. +func WirePattern(local string) string { + parts := strings.Split(local, ".") + for i, p := range parts { + if p == "**" { + parts[i] = ">" + } + } + return strings.Join(parts, ".") +} + // A ModuleStream is one module's declared stream as the bus holds it. type ModuleStream struct { Module string diff --git a/go/buscompose/names_test.go b/go/buscompose/names_test.go index c934da93..ba22fde5 100644 --- a/go/buscompose/names_test.go +++ b/go/buscompose/names_test.go @@ -33,6 +33,16 @@ func TestTheBusNamesAgree(t *testing.T) { if (Bucket{}).MaxBytes() != -1 || (Bucket{MaxMiB: 2}).MaxBytes() != 2<<20 { t.Fatal("a bucket's cap is not its max-mib in bytes") } + // Design 46 7-approval-mark §5: a stream's local event pattern as the bus spells it. + for local, wire := range map[string]string{"mail.**": "mail.>", "mail.*.message": "mail.*.message", "sent": "sent"} { + if got := WirePattern(local); got != wire { + t.Errorf("WirePattern(%q) = %q, want %q", local, got, wire) + } + } + // Design 46 7-approval-mark §13: a per-machine state reaches its machine's key and the keys below it, no other machine's. + if got := PerMachineKeys("ace"); len(got) != 2 || got[0] != "ace" || got[1] != "ace.>" { + t.Errorf("a per-machine state reaches %v on ace", got) + } // A seat's family is the shared events stream filtered to its subjects: a definition says the stream's age. for _, st := range MeshStreams() { if st.Name == EventsStream && int64(st.MaxAge) != seatdef.SharedEventsMaxAgeDays*24*60*60 { diff --git a/go/buscompose/nats.go b/go/buscompose/nats.go index f7f8ba8f..68757b4f 100644 --- a/go/buscompose/nats.go +++ b/go/buscompose/nats.go @@ -163,8 +163,8 @@ type Principal struct { // instances and read by whoever declares it. State []string Reads []string - // PerMachine is the local names of its state keyed by machine, reaching the key named for Node and - // no other; KeyedReads the keys of others' state it reads one key at a time (novox/hq ADR 0260). + // PerMachine is the local names of its state keyed by machine, reaching the keys of Node (PerMachineKeys) and + // no other machine's; KeyedReads the keys of others' state it reads one key at a time (novox/hq ADR 0260). PerMachine []string KeyedReads []KeyedRead // Logs is the local names of the logs this principal's module keeps (novox/hq ADR 0297). diff --git a/go/buscompose/state.go b/go/buscompose/state.go index 8e2d7340..c6ae74f2 100644 --- a/go/buscompose/state.go +++ b/go/buscompose/state.go @@ -18,8 +18,8 @@ type Bucket struct { History int // TTLSeconds is how long a value lives; zero is until replaced or deleted. TTLSeconds int - // PerMachine says each machine's instance writes only the key named for its machine (novox/hq ADR - // 0260), and is granted that key and no other. + // PerMachine says each machine's instance writes only the keys of its machine (novox/hq ADR 0260; design 46 + // 7-approval-mark §13): the key named for its machine and the keys below it, PerMachineKeys, and no other machine's. PerMachine bool // KeyTTL says the bucket allows a time to live per key (novox/hq ADR 0433 §1): its owner writes each key with its // own, and the server writes a delete marker, kept KeyTTLMarkerTTL, when it ends. @@ -36,6 +36,11 @@ func (b Bucket) MaxBytes() int64 { return int64(b.MaxMiB) * 1024 * 1024 } +// PerMachineKeys are the keys a machine's instance of a per-machine state reaches (design 46 7-approval-mark §13): the key +// named for its machine, as every module's per-machine key keeps its form, and the keys below it, `.>`; no other +// machine's. A membership's per-machine state (membership.StateIssued's Key) reaches the same. +func PerMachineKeys(node string) []string { return []string{node, node + ".>"} } + // BucketName is the bucket a module's state lives in: the module and the local name joined by an // underscore, which neither may contain, so two modules can never derive one bucket. func BucketName(module, name string) string { return module + "_" + name } diff --git a/go/membership/membership.go b/go/membership/membership.go index 8edb700d..0e493de2 100644 --- a/go/membership/membership.go +++ b/go/membership/membership.go @@ -112,7 +112,8 @@ type SeatServed struct { } // StateIssued is one bucket an assignment may reach, by the name its module uses for it (novox/hq ADR 0201). -// Key, when set, is the one key it may reach (novox/hq ADR 0260): this machine's. +// Key, when set, names the machine whose keys it may reach (novox/hq ADR 0260; design 46 7-approval-mark §13): that key and the +// keys below it (buscompose.PerMachineKeys), no other machine's. // // PerNode says the state is kept one bucket per node (novox/hq ADR 0593 §6: `composed-`): Bucket is then the // buckets' prefix, each bucket the prefix and a node's name, and a key's first part names its node and so its bucket.