Repository navigation
Conversation
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Approved, with one non-blocking maintenance suggestion: share the runtime-neutral deadline helper and its regression tests between Spin and Cloudflare to keep future race fixes aligned, while retaining runtime-specific timers and cancellation in each adapter.
dhruv8sh
left a comment
There was a problem hiding this comment.
This is a careful, well-tested rework of auction deadline handling. Replacing the dormant has_enforceable_total_request_deadline capability with a single select_until / wait_until contract that every adapter implements is the right shape. The default implementation refuses with Unsupported instead of falling back to an unbounded wait, which is the correct safety default. The nonblocking snapshot on expiry keeps bids that already arrived when the publisher origin is slow, and the mediator cutoff (the earlier of the auction deadline and the mediator's own timeout) is now checked at all three points in both the sync and split paths.
The adapter work holds up on a close read:
- Fastly: the timed
fastly_async_io::selectgets a positive timeout (.max(1)), so the host can never treat it as unbounded. Each partial body is capped, and so is the total across bodies. Invalid handles fail only their own provider. - Axum: handles are borrowed across
tokio::time::timeout, so expiry can't drop siblings that already finished. - Cloudflare and Spin: the final
try_recvcloses the race between the timer and the response channel.
The real-runtime tests under Viceroy, workerd and Spin are a big step up from stub-only coverage.
CI status: all checks pass.
None of the comments below is blocking. The one I'd most like a reply on is the Fastly-only cache-bypass rule (comment 1).
Share pending-operation ownership and deadline channel collection between Cloudflare and Spin while retaining runtime timers and cancellation. Require cache bypass in bounded HTTP test collection like Fastly, preserve healthy siblings, and update auction test providers. Replace four elapsed wall-clock assertions with checks that unbounded work was skipped. Clarify bid eligibility and coordinated Fastly SDK updates.
Preserve current creative asset/click settings, renderer diagnostics, and CI Node/domain checks alongside the reviewed deadline changes. Resolve the adapter lockfile entries by retaining web-time and main's removal of redundant direct JS dependencies. Keep the new click-rewrite configuration row and the original dispatch-deadline description.
aram356
left a comment
There was a problem hiding this comment.
Summary
Bounds auction response collection by an absolute deadline measured from dispatch on the Fastly, Axum, Cloudflare, and Spin adapters (#1015). I found no runtime defects in the deadline logic. Requesting changes so each inline finding below is addressed or answered before approval. The blocking one is the regression test that is meant to prove the auction clock is never restarted at collection: it passes even when collection does restart the clock.
3 of the inline comments below carry a one-click GitHub
suggestion— use Commit suggestion (or Add suggestion to batch for several at once) to apply them as commits on the PR branch. The remaining comments describe the fix in prose because the change touches multiple places or lines outside the diff and can't be auto-applied.
Blocking
🔧 wrench
- Split-collection test can't detect a restarted auction clock — see inline at
crates/trusted-server-core/src/auction/orchestrator.rs:6134
Non-blocking
♻️ refactor / 🤔 thinking / 🌱 seedling / ⛏ nitpick / 🏕 camp site
- ♻️ Sub-millisecond timed-select guard has no test — see inline at
crates/trusted-server-adapter-fastly/src/platform/deadline.rs:392 - 🤔 wrangler is installed without a lockfile — see inline at
.github/workflows/worker-auction-deadlines.yml:62 - 🌱 Single-provider restriction comment no longer says why — see inline at
crates/trusted-server-adapter-cloudflare/src/platform.rs:459 - 🌱
block_onoverwait/select_untilwould hang on worker adapters — see inline atcrates/trusted-server-core/src/platform/http.rs:356 - ⛏ Stale
reject_multi_provider_fanoutdoc — see inline atcrates/trusted-server-adapter-cloudflare/src/platform.rs:768 - ⛏ Fastly harness docs ask for a hashed binary path — see inline at
docs/guide/auction-testing.md:95 - ⛏ Spin body-limit tests don't exercise the adapter — see inline at
crates/trusted-server-adapter-spin/src/platform.rs:1423 - 🏕 New CI gates are missing from AGENTS.md — see inline at
.github/workflows/worker-auction-deadlines.yml:1
CI Status
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- Analyze (javascript-typescript): PASS
- Analyze (python): PASS
- Analyze (rust): PASS
- browser integration tests: PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- cargo fmt: PASS (required)
- cargo test: PASS (required)
- cargo test (axum native): PASS
- cargo test (cross-adapter parity): PASS
- cargo test (ts CLI, native) (macos-latest): PASS
- cargo test (ts CLI, native) (ubuntu-latest): PASS
- CI Node pin guard: PASS
- CLAUDE.md symlink guard: PASS
- CodeQL: PASS
- docs-proposal scripts: PASS
- format-docs: PASS (required)
- format-typescript: PASS (required)
- HTML EOF runtime (Fastly): PASS
- integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- ts dev lint domains (changed lines): PASS
- vitest: PASS
- worker-deadlines (cloudflare): PASS
- worker-deadlines (spin): PASS
| if: matrix.adapter == 'cloudflare' | ||
| shell: bash | ||
| run: | | ||
| npm install --global wrangler@4.83.0 |
There was a problem hiding this comment.
🤔 thinking — wasm-bindgen and Spin are checked against published digests a few lines down, but this line pins only wrangler's own version. wrangler's dependencies are resolved fresh on every run with no lockfile, so a third-party release can change what this job runs, and whether it passes, regardless of the PR. Permissions are read-only and the job has no secrets, so the risk is mostly reproducibility. A small committed package.json plus package-lock.json for the fixture tools, installed with npm ci --prefix <dir>, would give wrangler the same treatment.
There was a problem hiding this comment.
Agreed on the reproducibility gap. I tried a committed package.json + package-lock.json with npm ci. Locking wrangler@4.83.0 pins six transitive packages that npm audit flags (5 high: miniflare, undici, ws, sharp; 1 low: esbuild). Committing that lockfile would put those alerts on the repo, and it isn't covered by a Dependabot entry. integration-tests.yml on main also runs npm install -g wrangler@4.83.0 the same way, so locking only this job would leave the other one as it is.
I've left this one out of this PR. I'd rather handle it as one follow-up: a shared lockfile for both jobs on a current wrangler (4.156.0 audits clean), plus a Dependabot entry. Leaving this thread open in case you'd rather it land here.
Combine main's auction trace observations with dispatch-time deadline collection. Late planned completions no longer exist as a rejection path, so their trace hook is dropped; budget-exhausted providers and late mediator launches still record trace failures. The combined collection future overflowed the default 1 MiB WASI stack in the Viceroy publisher tests, so both publisher call sites now box it, matching the existing large-future pattern.
Shorten the split-collection select delays below the auction budget so a collection clock restarted after the origin wait collects the response and fails the test. Extract the Fastly timed-select rounding and the Spin bounded body collection into helpers that the production paths call, so their tests exercise real code. Explain the worker single-provider limit, the block_on hazard on worker executors, the self-building Fastly harness, and the new deadline CI gates.
Summary
Changes
core/src/auction/orchestrator.rsCrate paths above are relative to
crates/trusted-server-.fastly-sysis inherited from workspace dependencies at the locked SDK version;web-timesupplies the shared clock.Closes
Closes #1015
Spec
Not a feature. Implements #1015. Technical plan:
docs/superpowers/plans/2026-10-22-absolute-auction-deadlines.md.Deadline decisions and limits
Provider bids complete at the final nonblocking snapshot remain eligible even after
tmax; mediation starts only with remaining budget and rejects responses collected after its cutoff. This bounds additional network waiting rather than eligibility by an unavailable upstream arrival timestamp.Fastly does not use an empty handle list or millisecond header polling. One missing cache-bypass marker or malformed pending request fails only that provider, retaining healthy siblings. Retained payloads share a 10 MiB cap; pressure rejects the largest partial response under its own name. Payload vectors move directly into platform bodies. This cap excludes host/allocator overhead; the final nonblocking drain uses local CPU bounded by payload size, not a separate CPU-time ceiling.
Fastly closes acquired bodies and abandons pending requests because its installed SDK cannot cancel them. Other adapters abort owned tasks; Cloudflare also aborts fetch. Upstream side effects cannot be undone. Runtime scheduling and timer granularity can add overshoot; local parsing, ranking, HTML work, and delivery remain outside the network cutoff.
Review findings and proof
Test plan
Exploratory
tsc --noEmitand whole-repositoryactionlintreport pre-existing errors in unchanged files; they are not counted as passing checks. Local verification uses Rust 1.98.1, Viceroy 0.21.0, and Node 24.12.0; the repository pins Node 24.20.0. Runtime checks use local SDK runtimes, not production Fastly/Cloudflare deployments.Checklist