Skip to content

fix(ads): re-render stale ready spots in the background (gh-#854) - #861

Merged
genwave-radio merged 1 commit into
mainfrom
fix/ad-rerender-854
Sep 26, 2026
Merged

genwave-radio merged 1 commit into
mainfrom
fix/ad-rerender-854

Conversation

@genwave-radio

Copy link
Copy Markdown
Collaborator

Closes #854

🎯 What

Ready ad spots rendered by an older pipeline now re-render in the background, on their current cast and bed. A spot never leaves ready, so it keeps airing its old audio until the new audio is swapped in.

🔧 How

  • db/48 (mirrored in db/06, pinned) adds three columns to ad_spot:
    • render_version: AdRenderVersion.Current = 1, stamped by MarkReady.
    • pending_retire_media_id and pending_confirm_media_id: durable markers.
  • When it runs: the worker re-renders one stale spot per tick, and only after the approved queue is empty.
  • New audio is written switched off. A guarded station transaction then swaps it in:
    • The guard is state='ready' AND media_id=old AND no pending retire.
    • It first re-checks that the old row is still eligible and not never_play.
    • The same transaction stamps both markers.
  • Why the markers: station_svc has no privilege on the library schema, so the eligibility flips happen after commit. Each flip clears its own marker.
    • A missing row counts as done. A thrown write stays pending.
    • Every tick drains both markers:
      • The old row is turned off unless a spot references it again.
      • The new row is turned on only while its spot is still ready on it.
  • Why this matters: the ad picker airs any eligible ad media with no ad_spot join. So a displaced row left eligible would air forever, beyond the reach of pause or retire. The markers make that impossible, even across a crash.
  • Never done: operator-disabled or never_play rows are never revived, and old files are never deleted.
  • Skips and retries:
    • Budget timeouts put the spot in the skip set, so one slow spot can't starve the rest.
    • A break-window yield stays retryable.
    • Spots with a pending marker are not re-rendered.

✅ Verified

  • dotnet build GenWave.sln -warnaserror: 0 warnings.
  • Full solution with Category!=Integration: 0 failures.
  • Gh854 specs: 59 Ads, 45 MediaLibrary against real Postgres.
  • Dev-stack smoke:
    • Two render_version 0 spots (10 and 19) swapped one per tick.
    • Old media 18308 and 18310 went ineligible, with their files kept.
    • New media 18337 and 18338 are eligible.
    • Every marker cleared, and the api logged no errors.

📝 Notes for follow-up

  • A millisecond operator-retire race: it sits between the confirm guard and the eligible flip. The same race already exists on the first-render path.
  • Disk: each version bump leaves the old files and rows switched off, with no sweep.
  • Auditioned previews: a spot promoted from an auditioned preview is also re-rendered.

Ready spots rendered by an older pipeline now re-render on their current
cast and bed without ever leaving 'ready'. db/48 adds render_version plus
two durable markers (pending_retire_media_id, pending_confirm_media_id):
the guarded station-side swap stamps both, and every worker tick drains
them, so a displaced old row is always turned off and a new row is always
turned on (only while its spot is still ready on it), even across a crash
or a failed library write. Operator-disabled or never_play rows are never
revived; old files are never deleted.
@genwave-radio
genwave-radio merged commit 1dfdfec into main Sep 26, 2026
11 checks passed
@genwave-radio
genwave-radio deleted the fix/ad-rerender-854 branch September 26, 2026 14:23
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ads: an upgrade never re-renders ready ads — v5.12.0's ad fixes don't reach air

1 participant