Say mesh-grants.consumer-behind again only when the consumers behind change, not their counts

The counts change every pass, so a consumer that stays behind was said to the operator again at each pass. The
condition names the consumers alone; how far each is stays in the last pass.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 12:23:23 +01:00
parent 655692c7ba
commit 6a4d88265d
2 changed files with 16 additions and 2 deletions
+6
View File
@@ -2309,6 +2309,12 @@ func TestGrants(t *testing.T) {
len(behind) == 0 || behind[len(behind)-1].State != asks.StateRaised || !strings.Contains(behind[len(behind)-1].Explanation, "EVENTS.b_mod-c") {
t.Fatalf("a consumer far behind: %+v, said %+v", res.Consumers, behind)
}
// Still behind at the next pass, by another count: the condition names the consumers alone, so it is not said again.
made["pending"] = grants.BehindBound + 50
n := len(behind)
if res = p.Compose(); len(res.Consumers.Behind) != 1 || len(behind) != n {
t.Fatalf("a consumer still behind by another count was said again: %+v", behind[n-1:])
}
made["pending"] = grants.BehindBound
res = p.Compose()
if len(res.Consumers.Behind) != 0 || behind[len(behind)-1].State != asks.StateCleared {
@@ -256,7 +256,15 @@ func (p *Part) sayBehind(st *ConsumersState) {
if p.Say == nil {
return
}
why := strings.Join(st.Behind, "; ")
// The consumers alone decide whether it is said again, never how far each is behind, which changes every pass: the
// counts stay in the last pass's consumers.behind (its state user-list, key pass), and the condition is not said again
// for them.
var names []string
for _, b := range st.Behind {
name, _, _ := strings.Cut(b, ": ")
names = append(names, name)
}
why := strings.Join(names, ", ")
p.holdMu.Lock()
defer p.holdMu.Unlock()
if p.behindKnown && why == p.behindSaid {
@@ -270,7 +278,7 @@ func (p *Part) sayBehind(st *ConsumersState) {
c.Summary = fmt.Sprintf("%d durable consumers are more than %d messages behind their stream's head", len(st.Behind), BehindBound)
c.Headline, c.Needs = "Messages pile up for a listener", "a look at the module that reads them"
c.Explanation = asks.NeedsYou + "a look. These consumers stayed far behind their stream's head at two passes " +
"running, so what is sent to their modules waits unread: " + why
"running, so what is sent to their modules waits unread (how far each is, in the grants part's last pass): " + why
c.Resolved = "every account consumer is back within the bound"
}
if p.Say(c) == nil {