Say a message two walks held once, in the message of the walk whose limit is due (ADR 0505 §7)

This commit is contained in:
2026-10-22 14:39:35 +02:00
parent 3cf75580b5
commit ea8692b08f
2 changed files with 48 additions and 1 deletions
@@ -370,3 +370,40 @@ func TestAWalkSaidAgainWithItsDeclaredMachinesActsOnce(t *testing.T) {
t.Fatalf("its end was told %d times: %+v", n, dt.sends)
}
}
// ADR 0505 §7: a message two walks first held, the sooner of which ends at its limit, is said exactly once — in that
// walk's end message — and not again on its own for the other walk; once per channel when it is urgent.
func TestAMessageTwoWalksFirstHeldIsSaidOnceAtTheSoonerLimit(t *testing.T) {
for _, sev := range []string{Warning, Urgent} {
h, tg, dt, c, _ := newHolder(t)
walk(t, h, "plan-a", WalkStarted, "", c.now(), 360, novox("gitea")) // its limit: 6 min and the settle
walk(t, h, "plan-b", WalkStarted, "", c.now(), 1200, novox("mesh-issues")) // its limit: 22 min
k := cond("machine.novox.network", sev, "novox lost its default route")
k.Raised = c.now()
h.Condition(EventRaised, k)
runFor(h, c, 7*time.Minute)
walk(t, h, "plan-a", WalkEnded, WalkDelivered, c.now(), 0) // settles until its limit, 8 min after the hold
runFor(h, c, 3*time.Minute)
count := func(ch *fakeChannel) int {
n := 0
for _, m := range ch.sends {
if strings.Contains(m.Text(), headlineOf(k.Key)) {
n++
}
}
return n
}
want := map[*fakeChannel]int{dt: 1, tg: 0}
if sev == Urgent {
want[tg] = 1
}
for ch, n := range want {
if got := count(ch); got != n {
t.Fatalf("%s: said %d times on %s, want %d: %+v", sev, got, ch.name, n, ch.sends)
}
}
if !heard("Still open after the delivery of gitea to novox:", dt) {
t.Fatalf("%s: not told in the sooner walk's end message: %+v", sev, dt.sends)
}
}
}
+11 -1
View File
@@ -598,6 +598,16 @@ func (h *Holder) settledWalks(now time.Time) {
// due tells what it holds in its message instead. Called at each flush.
func (h *Holder) overdueHolds(now time.Time) {
h.mu.Lock()
// What a walk whose own end or bound is due holds is told in that walk's message, and nowhere else: one place
// says it, so a message two walks hold is never said twice.
tellsItself := map[string]bool{}
for _, w := range h.walks {
if !now.Before(w.holdEnd()) {
for key := range w.Held {
tellsItself[key] = true
}
}
}
due := map[string]HeldMessage{}
var touched []*Walk
for _, w := range h.walks {
@@ -607,7 +617,7 @@ func (h *Holder) overdueHolds(now time.Time) {
}
for key, hm := range w.Held {
if hm.past(now) {
if prev, ok := due[key]; !ok || hm.At.Before(prev.At) {
if prev, ok := due[key]; !tellsItself[key] && (!ok || hm.At.Before(prev.At)) {
due[key] = hm
}
delete(w.Held, key)