Files
mesh-delivery/modules/mesh-grants/internal/grants/full.go
T
jschoubben 0b03b9c7c5 mesh-grants: create a module's object buckets and history streams, grant their readers read-only, and warn at 75 percent
ADR 0592 §5, §6, §9, §25 (the grants part's share by ADR 0593 §10): an object bucket and a stream that
keeps its history are created create-only with their caps and no age limit, refusing new data when full;
the owner alone writes its bucket and a module its grant names reads it; the part raises
mesh-grants.history-full once any passes 75 percent. Each engine also reads the indexer's manifest
verdicts by one key for compose-candidate.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 18:56:19 +01:00

96 lines
3.6 KiB
Go

package grants
import (
"fmt"
"strings"
"git.novox.be/novox/mesh-sdk/go/asks"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// **A history stream or an object bucket past 75 percent of its cap is said, in one place for every module** (novox/hq
// ADR 0592 §6, §25; ADR 0593 §10 gives the controller's part to this part). Such a stream or bucket refuses a new message
// or object when full rather than dropping its oldest, so filling up is the one way it fails: the part reads every
// stream's size from the bus module's nats_streams at each pass (HeldStreams) and raises the warning FullCondition naming
// each one past catalogue.FullWarnPercent, cleared once none is.
// FullCondition is the key of the warning raised while a history stream or an object bucket is past its mark.
const FullCondition = "mesh-grants.history-full"
// Fill is how full one history stream or object bucket is: its module, its stream on the bus, its bytes and its cap.
type Fill struct {
Module string `json:"module"`
Stream string `json:"stream"`
Bytes int64 `json:"bytes"`
Cap int64 `json:"cap"`
}
// Percent is its bytes in percent of its cap, rounded down.
func (f Fill) Percent() int64 { return f.Bytes * 100 / f.Cap }
// Full is every history stream and object bucket of a placed module past catalogue.FullWarnPercent of its cap, by the
// sizes the bus holds (bytes, by stream name), in module and stream order. One the bus does not hold is not full. Pure.
func Full(in Inputs, bytes map[string]int64) []Fill {
var out []Fill
for _, module := range sortedKeys(in.Manifests) {
if !assigned(in, module) {
continue
}
m := in.Manifests[module]
var capped []Fill
for _, s := range m.Streams {
if s.KeepsHistory() {
capped = append(capped, Fill{Module: module, Stream: s.BusName(), Cap: int64(s.Cap()) << 20})
}
}
for _, o := range objectsOf(m) {
capped = append(capped, Fill{Module: module, Stream: "OBJ_" + o.Bucket(), Cap: int64(o.MaxMiB) << 20})
}
for _, f := range capped {
held, ok := bytes[f.Stream]
if !ok || f.Cap <= 0 {
continue
}
f.Bytes = held
if f.Bytes*100 >= catalogue.FullWarnPercent*f.Cap {
out = append(out, f)
}
}
}
return out
}
// sayFull raises FullCondition while a history stream or an object bucket is past its mark, and clears it once none is:
// said once per set of streams, again when that set changes, and at the first pass after a start.
func (p *Part) sayFull(full []Fill) {
if p.Say == nil {
return
}
var names, lines []string
for _, f := range full {
names = append(names, f.Stream)
lines = append(lines, fmt.Sprintf("%s of %s: %d of %d MiB (%d%%)", f.Stream, f.Module, f.Bytes>>20, f.Cap>>20, f.Percent()))
}
said := strings.Join(names, " ")
p.holdMu.Lock()
defer p.holdMu.Unlock()
if p.fullKnown && said == p.fullSaid {
return
}
c := asks.Condition{Key: FullCondition, Subject: asks.Scope{Scope: "module", ID: Module}}
if said == "" {
c.State = asks.StateCleared
} else {
c.Severity, c.State = asks.SeverityWarning, asks.StateRaised
c.Summary = fmt.Sprintf("%d history stream(s) or object bucket(s) past %d%% of their cap: %s", len(full),
catalogue.FullWarnPercent, strings.Join(lines, "; "))
c.Headline, c.Needs = "A stream that keeps its history is filling up", "a larger cap in the module's manifest, or data removed"
c.Explanation = asks.NeedsYou + "a decision before it is full: such a stream or bucket refuses new messages when full " +
"rather than dropping its oldest (novox/hq ADR 0592 §6). " + strings.Join(lines, "; ")
c.Resolved = "every history stream and object bucket is below its mark"
}
if p.Say(c) == nil {
p.fullSaid, p.fullKnown = said, true
}
}