From d58b717243129b4090c62b060137098b07ec7485 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 6 Nov 2026 10:33:49 +0100 Subject: [PATCH] A try's end is said on a context its own stop does not cancel (issue 1921) The end a run writes comes back on the key's watch, where Put stops the run's context and leaves the saying to the run, so the end was never said and the indexer kept the try's candidate. The group try's unit test now echoes the end on the watch while the run says it. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- .../mesh-delivery-checker/internal/check/try.go | 5 ++++- .../internal/check/try_test.go | 14 +++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/mesh-delivery-checker/internal/check/try.go b/modules/mesh-delivery-checker/internal/check/try.go index 8109a525..577f4d38 100644 --- a/modules/mesh-delivery-checker/internal/check/try.go +++ b/modules/mesh-delivery-checker/internal/check/try.go @@ -711,7 +711,10 @@ func (tr *Tryer) run(ctx context.Context, key string) { return } tr.logf("%s: %s", key, t.State) - tr.sendEnd(ctx, key) + // The end just written comes back on the key's watch, and Put then stops this run's context (it stops judging a try + // ended by hand) while this run says the end, which Put's own say leaves to it: the end is said on a context that + // stop does not cancel, or it is never said and the indexer keeps the candidate of the try's group. + tr.sendEnd(context.WithoutCancel(ctx), key) } // sendEnd says the end of an ended try whose start was said (Told) with the state it ended in, then clears Told in the try's diff --git a/modules/mesh-delivery-checker/internal/check/try_test.go b/modules/mesh-delivery-checker/internal/check/try_test.go index d0863d93..a5de63fb 100644 --- a/modules/mesh-delivery-checker/internal/check/try_test.go +++ b/modules/mesh-delivery-checker/internal/check/try_test.go @@ -188,7 +188,19 @@ func TestATryIsJudgedAsAGroupsCheckAndOwesTheForgeNothing(t *testing.T) { // ADR var waiting string cands.during = func() { waiting = readTry(t, kv, id).Waiting } // ADR 0639 ยง5: the stored try says what it waits for var said []string - tr := &Tryer{Tries: written, Judge: b.ex, Say: func(_ context.Context, event string, e tryevent.Event) error { + var tr *Tryer + echoed := false + tr = &Tryer{Tries: written, Judge: b.ex, Say: func(ctx context.Context, event string, e tryevent.Event) error { + if event == tryevent.Ended && !echoed { + // The end the run wrote comes back on the key's watch while the run says it: Put stops the run's context and + // leaves the saying to the run, whose say must not be stopped with it (issue 1921). + echoed = true + raw, _, _, _ := kv.Get(context.Background(), id) + tr.Put(context.Background(), id, raw) + } + if err := ctx.Err(); err != nil { + return err // a publish on a stopped context is never acknowledged + } said = append(said, fmt.Sprintf("%s %s %d %s", event, e.ID, len(e.Members), e.State)) return nil }}