Age a restricted stream's dead letters out with it and never show their body (issue 728)

DEAD_LETTERS kept a message given up on MAIL forever and showed it whole to any caller of dead-letters, outside the grant and past MAIL's week. Per ADR 0376 §3-§6: the kept copy carries a per-message time to live of what is left of its stream's age (a copy already past it is kept without its body), DEAD_LETTERS and EVENTS allow per-message TTL, the list and show give no body or own headers for it, delivering it again needs a minute left and the copy keeps the time left, and its max-deliveries condition clears saying how many aged out undelivered and from which stream.
This commit is contained in:
jochen
2026-10-15 14:11:37 +02:00
parent f75cd0239d
commit d03e24f423
10 changed files with 690 additions and 7 deletions
+12 -1
View File
@@ -91,7 +91,10 @@ type signalFacts struct {
// deadLettersNewest is when DEAD_LETTERS kept the newest message it holds for each of those
// consumers: the condition counts the messages given up on, not the looks (novox/hq issue 440).
deadLettersNewest map[string]time.Time
advisoriesErr error
// deadLettersAging is, for each consumer whose dead letters came from a restricted stream, when each ages out
// (novox/hq ADR 0376 §6); nil when it was not read.
deadLettersAging map[string][]link.Aging
advisoriesErr error
// busFaultsErr is why the bus module's events could not be read this tick (novox/hq ADR 0357).
busFaultsErr error
@@ -211,6 +214,9 @@ type watchdogs struct {
// confirm holds back a row blind for one tick: a read that timed out once on a loaded store.
confirm confirming
// aging is what is remembered of restricted dead letters between looks (novox/hq ADR 0376 §6).
aging agingTally
mu sync.Mutex
ticked time.Time
last *signalFacts
@@ -264,6 +270,8 @@ func (w *watchdogs) tick(ctx context.Context) {
func (w *watchdogs) see(running context.Context, f *signalFacts) {
var problems []string
var blind []conditions.Observation
// What aged out of DEAD_LETTERS, said and cleared in its own words before the rows clear it in theirs.
problems = append(problems, w.sayAgedOut(running, f)...)
for _, row := range watchedRows() {
if err := row.needs(f); err != nil {
blind = append(blind, blindRow(row, err))
@@ -382,6 +390,9 @@ func (w *watchdogs) gather(ctx context.Context) *signalFacts {
}
}
}
if f.advisoriesErr == nil && w.js != nil {
f.deadLettersAging, f.advisoriesErr = link.AgingDeadLetters(w.js.Context(), f.deadLetters)
}
f.handActs, f.handActsErr = w.gatherHandActs(ctx, now)
f.refusedSends, f.refusedSendsErr = inv.RefusedSendsSince(ctx, now.Add(-generationRefusalsSaid))
f.doing, f.doingErr = inv.NotDoingWhatTheyWereTold(ctx)