Skip to content

Fix: unsatisfied-slot safety pause can live-lock and permanently over-count - #62

Open
sebclark wants to merge 1 commit into
Chaptarr:developfrom
sebclark:fix-mam-reservation-livelock
Open

Fix: unsatisfied-slot safety pause can live-lock and permanently over-count#62
sebclark wants to merge 1 commit into
Chaptarr:developfrom
sebclark:fix-mam-reservation-livelock

Conversation

@sebclark

@sebclark sebclark commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

The bug (user-reported)

The safety pause counts the tracker's reported unsatisfied count + local in-flight reservations. Reservations are retired by Reconcile once the provider's summary snapshot postdates the reservation's accounting window (ReservedUtc + 20 min). But retrying an unconfirmed reservation deliberately refreshes ReservedUtc (per retry_should_refresh_an_unconfirmed_reservations_accounting_window) — so a release that keeps failing and re-grabbing slides its window forward forever and its reservation becomes immortal:

  1. N releases fail/retry repeatedly → their reservations never retire
  2. The account reads permanently N over → the safety pause rejects everything else
  3. Which keeps the same N releases retrying → the loop sustains itself

Restarts and the account-status refresh task don't help because the reservation rows persist in the database and the slide resumes. Symptom: the reported unsatisfied number sits exactly N above the tracker's own count, indefinitely.

Fix

Keep the window-refresh behaviour (the existing test still passes untouched) but add an immutable anchor and a hard ceiling:

  • Migration 103 adds nullable FirstReservedUtc, backfilled from ReservedUtc
  • Inserts stamp it; retries preserve it (??= before the slide)
  • Reconcile unconditionally retires any reservation older than 24h from its first attempt, with an Info log naming the torrent

A reservation that genuinely satisfies still retires via the existing snapshot rule long before the ceiling; the ceiling only catches the pathological retry loop.

Tests

  • reservation_should_retire_after_maximum_lifetime_despite_retries — just-retried row (snapshot rule can never cover it) with a first attempt beyond the ceiling → retired
  • reservation_within_maximum_lifetime_should_survive_a_stale_snapshot — young reservation is untouched
  • retry_should_preserve_the_first_reserved_anchor — the slide keeps refreshing ReservedUtc but the anchor holds (also backfills legacy rows on first retry)
  • Existing indexer suite intact: 88/88 passing

Note: no fails-first repro test for this one — the fix adds a schema column, so a pre-fix repro wouldn't compile. The live-lock derivation above plus the invariant tests are the evidence.

@sebclark sebclark changed the title Fix: MAM safety pause can live-lock, permanently over-counting unsatisfied slots Fix: unsatisfied-slot safety pause can live-lock and permanently over-count Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant