Files
mesh-catalog/modules/nats/internal/keeper/fill.go
T
jschoubben 1af1a7b002 nats: the keeper wakes on decided, spends a warrant once, and says again what the bus did not take
Review round 1 of the registry milestone (design 25 § 12.2, § 12.3, § 12.7):
- the fill pass runs from the start, while the first view is asked;
- recreate and drop wake on the operator channel's decided on the keeper's durable consumer, never a minute's tick;
- a warrant is spent by compare-and-set (acted.<ask id> in requests) before anything is deleted; a second request on it
  is refused; a request whose ask ended unanswered is ended, said; the ask is raised before the request is handed;
- created is marked only once said; an event or a condition the bus did not take is kept, named by the health and
  said again at the next fill pass with its one id, on the subjects left (the double publish);
- the health judges the first view past its bound and never skips an unread consumer backlog;
- an unrecordable hand act is said on the request; nats_requests reads what waits, what ended and each warrant spent,
  and drops a waiting request by hand; a tool waits for its request's end on one watch of requests;
- the whole-module test reads the warrant from the router's record under a process connection.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 17:19:54 +01:00

75 lines
2.2 KiB
Go

package keeper
import (
"context"
"fmt"
"time"
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
)
// FillPass reads the fill of every entry that refuses when full (ADR 0595 §6), raising `full` at the raise mark and
// clearing it only below the clear mark, so an entry at the mark does not flap. It is the keeper's one periodic read.
func (k *Keeper) FillPass() {
// What the bus did not take before is said again first (design 25 § 12.2).
k.SayUnsaid()
raise, clear := k.marks()
keys, err := k.Store.Keys(StateAsked)
if err != nil {
k.logf("the fill pass cannot read its own %s: %v", StateAsked, err)
return
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
for _, module := range keys {
held, _, found := k.asked(module)
if !found {
continue
}
changed := false
for _, local := range sortedKeys(held.Made) {
m := held.Made[local]
if !m.Created {
continue
}
var most int64 = -1
for _, o := range m.Plan.Objects {
if !o.Settings.Refuses() || o.Settings.MaxBytes <= 0 {
continue
}
_, f, there, err := k.Bus.Held(ctx, o)
if err != nil || !there {
continue
}
if p := int64(f.Bytes) * 100 / o.Settings.MaxBytes; p > most {
most = p
}
}
if most < 0 {
continue
}
was := m
m.Fill = most
switch {
case !m.Full && most >= raise:
m.Full = true
k.emit(EventFull, m.Plan, held.Revision, fmt.Sprintf("%d%% of its cap, past the raise mark %d%%", most, raise), nil, nil)
k.raise(conditionFull, module, local, occ.SeverityWarning, "Kept data is filling up",
"a larger cap in the module's manifest, or data removed",
fmt.Sprintf("%s of %s holds %d%% of its cap and refuses new data when full; it is cleared below %d%%", local, module, most, clear))
case m.Full && most < clear:
m.Full = false
k.emit(EventFullCleared, m.Plan, held.Revision, fmt.Sprintf("%d%% of its cap, below the clear mark %d%%", most, clear), nil, nil)
k.clear(conditionFull, module, local)
}
if m.Fill != was.Fill || m.Full != was.Full {
held.Made[local], changed = m, true
}
}
if changed {
k.write(held)
}
}
k.setStatus(func(s *Status) { s.Pass = k.now() })
}