Zum Inhalt springen

Process 06.2 — Reveal ceremony broadcast

Process 06.2 — Reveal ceremony broadcast

Section titled “Process 06.2 — Reveal ceremony broadcast”

Sequence for the 00:00 UTC reveal ceremony, when the winning ad for date D activates and the public landing page plays the cinematic.

Clarification vs master design. Master §1 specifies “Every day at 00:00 UTC a sealed-bid auction closes for an ad slot 24 hours later” (close) and §5 acceptance gate item 8: “at 00:00 UTC T, the winning creative is the Spotlight” (activation). The cinematic reveal animation is therefore triggered by the activation message at 00:00 UTC of day T — not by the close at 00:00 UTC of day T−1. The same 00:00 UTC wall-clock instant fires both the previous auction’s close (saga starts) and the previous day’s saga-activation (today’s ad goes live). This process diagram covers the activation reveal branch.

After the winning advertisement becomes active, the system updates the day mirror and clears cached pages. The WebSocket hub immediately broadcasts the reveal so lobby and day-page clients show the new Spotlight.After the winning advertisement becomes active, the system updates the day mirror and clears cached pages. The WebSocket hub immediately broadcasts the reveal so lobby and day-page clients show the new Spotlight.

Why HubDO does the lobby fan-out, not each AuctionDO.

  • Lobby viewers are subscribed to HubDO("lobby"), not to any specific AuctionDO. They learn “today’s winner” from a single message in a single DO. This bounds the broadcast to one DO’s ~1 k msg/s envelope and lets us chunk-pace within it.
  • AuctionDO(D)‘s remaining subscribers receive the terminal auction_closed close-out from the auction close path, not from the reveal ceremony. Browser UI handling for combining reveal and terminal auction state is tracked in #230.

Thundering-herd mitigations applied here.

  1. Chunked fan-out — 256 sockets per loop iteration, await scheduler.wait(0) between chunks to yield to the runtime. Prevents a single synchronous burst from starving other DO work.
  2. CF cache purge before broadcast — the message tells the client “the new ad is live”; if a client fetches / in response, the edge already serves the new HTML, no origin stampede.
  3. OG image pre-rendered during saga’s ACTIVATE step, well before this fan-out — no live image-render under load.
  4. Client animation is local — the WS message carries winning_ad_id; the creative HTML for /day/D is already in CF cache. No back-and-forth.
  5. Bypass-batch but still uses the envelope+seq machinery — clients in replay state during reconnect after the storm still see spotlight_revealed in order.

Failure modes & responses.

  • HubDO unreachable mid-fan-out. Some lobby sockets miss the message. They reconnect (visibilitychange or backoff), send last_seq, get a replay including spotlight_revealed from the HubDO ring buffer. Worst-case 5–8s late cinematic — acceptable; the ad itself is live regardless.
  • Activation but no spotlight_revealed (Hub broadcast fails entirely). Public site shows the new ad on next page load (cache-purged); cinematic is best-effort, the data layer is authoritative. No saga retry needed for the WS step.
  • Saga retries ACTIVATE. announceReveal is idempotent per (date, winning_ad_id); HubDO dedupes on that key before incrementing seq.

Files referenced.

  • apps/cron/src/index.ts — 00:00 UTC scheduled trigger.
  • packages/saga/src/campaign-saga.ts — ACTIVATE step calls env.WS_HUB.fetch("/internal/announce-reveal", ...) via service binding.
  • apps/ws/src/index.ts — POST /internal/announce-reveal route, gated by a shared secret/service-binding header.
  • apps/ws/src/websocket-hub.ts — announceReveal({date, winning_ad_id}) parse, idempotency, ring append, and chunked fan-out.
  • apps/api/src/auction-do.ts — terminal flush + auction_closed envelope for the auction close path.