144 lines
7.2 KiB
Go
144 lines
7.2 KiB
Go
package buscompose
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/health"
|
|
)
|
|
|
|
// **The health holders' grants** (novox/hq ADR 0564 clauses 2, 5 and 8, Limits 2, 4 and 8; issue 1952 S5).
|
|
//
|
|
// - The condition keeper reads the controller's open conditions until the lease write, for what clauses 8 and 9 carry
|
|
// from them, and mesh-healers the controller's hand-act log, for S15 (Limits 2): both buckets the controller
|
|
// announces readable by nobody, so each is read by its named reader alone. The keeper, mesh-healers and the
|
|
// watchman read the controller's lease (Limits 8). Nobody reads mesh-healers' `would` (clause 8): what the healers
|
|
// would have done reaches no condition. Every observer's `findings`, the healers' `attempts`, the wants and the
|
|
// haves are read through `reads` as any announced state is; and no module writes another's state, so an observer
|
|
// can never write the keeper's buckets (clause 2).
|
|
// - The watchman makes its own consumer on DEAD_LETTER_NOTICES, by its own name, pulled, and reads whether the
|
|
// controller's consumer `controller` still stands; it may neither delete nor change that consumer (clause 5). It
|
|
// lists and shows DEAD_LETTERS, which is all it does before the lease write.
|
|
// - The bus module's tools run on a process account of their own, BusToolsProcess, which may remove only the
|
|
// consumer `controller` on DEAD_LETTER_NOTICES and keys of the node-engine's acts bucket (Limits 4), never the
|
|
// backup's account `broker`, which only reads (ADR 0235).
|
|
|
|
// The states the composer names its readers for, beside the lease (NamedReaders).
|
|
var (
|
|
// ControllerConditions is the controller's open conditions, read by the keeper alone (ADR 0564 clauses 8, 9).
|
|
ControllerConditions = ControllerSeat + ".conditions"
|
|
// ControllerHandActs is the controller's hand-act log, read by mesh-healers alone of the four health holders (ADR
|
|
// 0564 Limits 2).
|
|
ControllerHandActs = ControllerSeat + ".hand-acts"
|
|
// HealersWould is what mesh-healers would have done while the lease names a controller: read by nobody (clause 8).
|
|
HealersWould = health.HealersModule + "." + health.WouldState
|
|
)
|
|
|
|
// BusToolsProcess is the process of the module holding mesh-broker whose account removes from the bus (ADR 0564 Limits
|
|
// 4): its secret is broker-nats-tools, as every process account's is broker-<process id> (ADR 0436 §2).
|
|
const BusToolsProcess = "nats-tools"
|
|
|
|
// BusRemovalGrants is the whole authority of the bus module's tools account (ADR 0564 Limits 4, issue 1952 notes 6 and
|
|
// 7): read and delete the controller's consumer on DEAD_LETTER_NOTICES (the lease write's step, clause 5), and read and
|
|
// remove keys of the node-engine's acts bucket, which nothing else may empty when it fills. STREAM.INFO with a subject
|
|
// filter lists what a removal would take, so the tools read before they remove. A further consumer or bucket is a change
|
|
// of this grant.
|
|
func BusRemovalGrants() []string {
|
|
return []string{
|
|
"$JS.API.CONSUMER.INFO." + DeadLetterNoticesStream + "." + ControllerName,
|
|
"$JS.API.CONSUMER.DELETE." + DeadLetterNoticesStream + "." + ControllerName,
|
|
"$JS.API.STREAM.INFO.KV_" + EngineActsBucket,
|
|
"$KV." + EngineActsBucket + ".>",
|
|
}
|
|
}
|
|
|
|
// removesEngineActs is the bus module's tools account removing keys of the node-engine's acts bucket: the writers
|
|
// table's one exemption from a node's engine being the only writer of its keys, for removal (ADR 0564 Limits 4).
|
|
func removesEngineActs(p Principal, pattern string) bool {
|
|
return p.Kind == KindProcess && p.RemovesFromTheBus && pattern == "$KV."+EngineActsBucket+".>"
|
|
}
|
|
|
|
// WatchmanDeadLetterConsumer is the watchman's own consumer on DEAD_LETTER_NOTICES, made only once the lease names no
|
|
// controller and `controller` is gone (ADR 0564 clause 5).
|
|
const WatchmanDeadLetterConsumer = WatchmanModule
|
|
|
|
// WatchmanDeadLetterGrants are what the watchman may publish for the dead letters (ADR 0564 clause 5): its own pulled
|
|
// consumer on DEAD_LETTER_NOTICES (made, read, pulled, acknowledged), whether `controller` still stands (read only), a
|
|
// read of DEAD_LETTERS (its list by subject, one kept message, an ordered consumer of its own to page them, a drop), the
|
|
// copies kept and the messages delivered again (`mesh.events.dead.>`, `mesh.again.>`), and on each stream a notice can
|
|
// name (sources: EVENTS, the seats' work queues and the modules' own) the message given up on read, its consumer read,
|
|
// and its message deleted from the work queue when delivered again, each stream by its name. Never a wildcard over the
|
|
// streams and never a bucket's or an object store's stream: a read of any message would read every bucket (the lead,
|
|
// 2026-10-29, on issue 1952 S5).
|
|
func WatchmanDeadLetterGrants(sources []string) []string {
|
|
own := DeadLetterNoticesStream + "." + WatchmanDeadLetterConsumer
|
|
out := []string{
|
|
"$JS.API.STREAM.INFO." + DeadLetterNoticesStream,
|
|
"$JS.API.CONSUMER.INFO." + DeadLetterNoticesStream + "." + ControllerName,
|
|
"$JS.API.CONSUMER.CREATE." + own,
|
|
"$JS.API.CONSUMER.INFO." + own,
|
|
"$JS.API.CONSUMER.MSG.NEXT." + own,
|
|
"$JS.ACK." + own + ".>",
|
|
"$JS.API.STREAM.INFO." + DeadLettersStream,
|
|
"$JS.API.STREAM.MSG.GET." + DeadLettersStream,
|
|
"$JS.API.STREAM.MSG.DELETE." + DeadLettersStream,
|
|
"$JS.API.CONSUMER.CREATE." + DeadLettersStream + ".>",
|
|
"$JS.API.CONSUMER.DELETE." + DeadLettersStream + ".*",
|
|
"$JS.FC." + DeadLettersStream + ".>",
|
|
deadLetterPrefix + ">",
|
|
againPrefix + ">",
|
|
}
|
|
for _, s := range DeadLetterSources(sources) {
|
|
out = append(out, "$JS.API.STREAM.MSG.GET."+s, "$JS.API.STREAM.MSG.DELETE."+s, "$JS.API.CONSUMER.INFO."+s+".*")
|
|
}
|
|
return out
|
|
}
|
|
|
|
// DeadLetterSources are the streams of names a dead letter may come from, sorted, none twice: each a plain name, never a
|
|
// bucket's (KV_) or an object store's (OBJ_) stream, the dead letters' own two streams, or a pattern.
|
|
func DeadLetterSources(names []string) []string {
|
|
seen := map[string]bool{}
|
|
var out []string
|
|
for _, n := range names {
|
|
switch {
|
|
case seen[n], !safeSubject.MatchString(n), strings.HasPrefix(n, "KV_"), strings.HasPrefix(n, "OBJ_"),
|
|
n == DeadLettersStream, n == DeadLetterNoticesStream:
|
|
continue
|
|
}
|
|
seen[n] = true
|
|
out = append(out, n)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// deadLetterSourcesOf are the streams the records' consumers give up on: EVENTS, the work queue of every seat that
|
|
// accepts work, and every module's and process's own work queue.
|
|
func deadLetterSourcesOf(r Records) []string {
|
|
out := []string{EventsStream}
|
|
for _, mods := range r.Assigned {
|
|
for _, d := range mods {
|
|
for _, s := range append(append([]Seat(nil), d.Holds...), d.Uses...) {
|
|
if len(s.Accepts) > 0 {
|
|
out = append(out, seatStreamName(s.Name))
|
|
}
|
|
}
|
|
for _, q := range d.Queues {
|
|
out = append(out, q.Stream)
|
|
}
|
|
for _, pr := range d.Processes {
|
|
for _, q := range pr.Queues {
|
|
out = append(out, q.Stream)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return DeadLetterSources(out)
|
|
}
|
|
|
|
// keepsDeadLetters is the watchman keeping the dead letters: a second writer of the copies and the deliveries again
|
|
// beside the controller, which acts until its lease is handed away and the watchman after (ADR 0564 clause 5).
|
|
func keepsDeadLetters(p Principal, pattern string) bool {
|
|
return isController(p, pattern) || (p.Kind == KindModule && p.Module == WatchmanModule)
|
|
}
|