The keeper's health reads the consumers it looks for: the shared monitoring read asks for them
mesh/delivery merged: source.main moved to novox/mesh-catalog@08fbc51af26f
mesh/check checking 1ef1b1356044 on 08fbc51af26f

nats_keeper_health found the keeper's consumer novox_nats_keeper in /jsz, but the read it shares with the
broker verbs asked /jsz?accounts=1&streams=1&config=1, without consumers=1, so keeperPending always read -1
and the keeper was "unhealthy: what waits on its consumer cannot be read from the bus's monitoring" for
ever (2026-11-08, issue 1966).

Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5
This commit is contained in:
2026-11-08 23:55:32 +01:00
parent 7fd4ed5898
commit 1ef1b13560
2 changed files with 18 additions and 1 deletions
+6 -1
View File
@@ -172,13 +172,18 @@ var ReadingVerbs = []string{"list", "show", "check", "orphans"}
// and kept in full on the bus module's own stream broker-events (BROKER_EVENTS), which `history` reads (keep.go).
var Events = []string{"created", "refused", "drifted", "drift-cleared", "full", "full-cleared", "orphaned", "recreated", "dropped"}
// heldJsz is the monitoring read the broker's verbs and the keeper's health share: with the consumers, since the
// keeper's health reads what waits on its own consumer from it (keeperPending); without them it read -1 and the keeper
// was never healthy (2026-11-08, issue 1966).
const heldJsz = "/jsz?accounts=1&streams=1&consumers=1&config=1"
// allTools is every tool and seat verb this bundle serves.
func allTools() []stdio.Tool {
monitor := LiveMonitor()
jsz := func() ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
return monitor(ctx, "/jsz?accounts=1&streams=1&config=1")
return monitor(ctx, heldJsz)
}
out := append(tools(monitor, ReadUsers, UsersWritten), removalTools(LiveBus(), SaidByTheRuntime, HeldInState)...)
out = append(out, expiryTools(LiveBus(), SaidByTheRuntime, RecordedBySeat)...)
+12
View File
@@ -277,3 +277,15 @@ func TestTheRemovalToolsRefuseBeforeTheyReachTheBus(t *testing.T) {
}
}
}
// The keeper's health reads what waits on its own consumer from the shared monitoring read: that read asks for the
// consumers, or the health answers "cannot be read" for ever (2026-11-08, issue 1966).
func TestTheHeldReadAsksForTheConsumers(t *testing.T) {
if !strings.Contains(heldJsz, "consumers=1") {
t.Fatalf("the monitoring read %q asks for no consumers, so keeperPending never finds the keeper's", heldJsz)
}
doc := []byte(`{"account_details":[{"stream_detail":[{"name":"EVENTS","consumer_detail":[{"name":"novox_nats_keeper","num_pending":2}]}]}]}`)
if got := keeperPending(doc); got != 2 {
t.Fatalf("keeperPending = %d on a read with the keeper's consumer, want 2", got)
}
}