Subtask B: the teller says an assigner ask written again under the id the router kept is not asked, with a row per kind that a new asking is asked again (issue 1966)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-09 22:48:02 +01:00
parent 2469531c2f
commit 5ab0d8ca88
2 changed files with 49 additions and 0 deletions
@@ -168,6 +168,10 @@ func (t *Teller) raiseAssignerAsk(key string) {
}
if open(k) {
t.catchUp(id)
} else {
// Said, never silent (issue 1966: a record ask taken back when its key went was written again under the same id on
// 2026-11-09 and nothing said why the operator was never asked): the assigner numbers each writing (asks.Ask.Asking).
t.logf("%s: the ask %s ended (answered, lapsed or taken back) and the assigner wrote the key again under the same id: not asked again, the router would drop it; a writing under a new asking is asked", key, id)
}
return
}
@@ -209,3 +209,48 @@ func TestABurstOfHoldsLeavesRoomForAMergeAsk(t *testing.T) {
t.Errorf("asked %v after the merge ask was answered, want no further hold", got)
}
}
// Issue 1966 (nats on novox): an assigner ask taken back when its key went is asked again when the assigner writes the key
// again under a new asking, whatever its kind; written again under the id the router kept it is not (the router would drop it)
// and the teller says so, never silent.
func TestAnAssignerAskWrittenAgainAfterBeingTakenAwayIsAskedAgainUnderANewAsking(t *testing.T) {
digest := "sha256:" + strings.Repeat("e", 64)
fp := "src2:" + strings.Repeat("a", 64)
recordKey, _ := asks.RecordAskKey("novox", "nats", digest)
restoreKey, _ := asks.RestoreAskKey("novox", "nats", fp)
holdKey, _ := asks.HoldAskKey("nats", fp)
for kind, key := range map[string]string{"record": recordKey, "restore": restoreKey, "hold": holdKey} {
t.Run(kind, func(t *testing.T) {
write := func(assigner memGate, asking int) oc.Ask {
ask := oc.Ask{ID: meshdeliveryclient.AskingID(key, asking), Headline: "nats waits", Explanation: "e", Who: occ.Operator,
Expires: occ.FormatTime(now.Add(occ.ApproveLasts)), OnExpiry: "stays",
Options: []oc.Option{{ID: "yes", Label: "Yes", Does: "d", Level: occ.Acknowledge}, {ID: "no", Label: "No", Does: "d", Level: occ.Acknowledge}}}
value, err := asks.EncodeAsk(key, asks.Ask{Ask: rawAsk(t, ask), Asking: asking})
if err != nil {
t.Fatal(err)
}
assigner[key] = value
return ask
}
b := newBed(t)
assigner, _ := withView(b)
first := write(assigner, 1000)
b.t.OnAssignerAsk(key)
delete(assigner, key)
b.t.OnAssignerAskGone(key) // the assigner took it away as stale: the router ask is taken back
second := write(assigner, 1060)
b.t.OnAssignerAsk(key)
if got := b.askedIDs(); len(got) != 2 || got[0] != first.ID || got[1] != second.ID || first.ID == second.ID {
t.Fatalf("asked %v, want %s then %s; log:\n%s", got, first.ID, second.ID, b.logs.String())
}
// Written again under the id the router kept, after it was taken back: not asked, and said.
delete(assigner, key)
b.t.OnAssignerAskGone(key)
write(assigner, 1060)
b.t.OnAssignerAsk(key)
if got := b.askedIDs(); len(got) != 2 || !strings.Contains(b.logs.String(), "wrote the key again under the same id") {
t.Errorf("asked %v after the same id was written again; log:\n%s", got, b.logs.String())
}
})
}
}