Add Prebid ad-latency optimizations design spec - #1053
Draft
aram356 wants to merge 12 commits into
Draft
Conversation
Three Trusted-Server-side levers for client-side auction properties, grounded in an instrumented local baseline against the autoblog origin: an opt-in requestBids coalescing window, a securepubads preconnect hint, and an opt-in first-refresh-auction prefetch at DOMContentLoaded that keeps the React-hydration-safe DOM application point unchanged.
Lever C: the window.load gate is installSlimPrebidLoader (the auction module is not loaded before load), so the prefetch lives in the unified GPT bundle and slim-Prebid consumes the stashed response; the double-rAF defer belongs to the SSAT adInit path, not this flow. Lever A: Prebid 10 requestBids returns a promise, so held calls return a promise settling with the merged auction instead of undefined.
aram356
marked this pull request as draft
August 21, 2026 04:37
Reframe Lever A as a load/QPS lever (measured burst POSTs were already concurrent) with absolute deadlines, per-caller bid-map partitioning, bookkeeping-before-callbacks ordering, synthetic-refresh and client-side-bidder exclusions, disjoint-code and payload-size admission bounds, and full promise semantics. Gate Lever B behind [integrations.gpt] gam_preconnect, correct the pubads_impl and crossorigin claims, and note the pre-consent connection decision. Demote Lever C to discovery-first: the described lifecycle seam does not exist, responses cannot be stashed without the requesting auction's bid-request IDs, consent readiness needs a concrete API contract that distinguishes consent-denied no-bids, and fallbacks must share the in-flight promise rather than duplicate auctions. Add a billing and impression integrity section, correct config-blob compatibility to the raw-JSON retention layer, and expand testing, measurement methodology, and one-lever-at-a-time rollout.
Gate Lever A behind a burst-trace prerequisite (the baseline never recorded the burst calls' option keys or codes; same-code duplicate auctions would make the disjoint-code rule merge nothing), reframe bidder-QPS reduction as a hypothesis, document the shared-auction global-targeting caveat, make the deadline algorithm executable (bidderTimeout captured at enqueue, 50ms tolerance, monotonic flush at min(windowEnd, earliestDeadline - 100ms), 50ms dispatch floor so Prebid's timeout||bidderTimeout fallback never sees 0), add dispatch-time revalidation against in-place mutation with exact batch bounds, keep per-caller registration IDs so throw-rollback semantics survive merging, and generalize the flush-first rule to every ineligible arrival. Rewrite billing integrity per delivery path (the PUC bridge is not this scope's boundary; requestAds couples fetch to render; PBS server-side notices may bill off-render). Name tsjs.requestAds as Lever C's leading discovery hypothesis and add cookie-parity consent, server-owned equivalence inputs, and server-owned cache tokens. Add Lever B governance (net-log proof, GPC and denied-CMP cases, named approver, insertion-order test). Make measurement paired and thresholded, note the telemetry arm-label gap and read-once config rollback semantics, and sanitize property naming.
Resolve the integrity contradiction by consistently accepting cross-caller delivery within a merged auction (forbidden only for units no caller requested) and adding callback-discipline capture plus the pending-state/synthetic-auction net-effect estimate to Phase 0. Make the queue reentrancy-safe via atomic batch detachment before dispatch, reframe deadlines as a reduced auction-time budget with a solo-dispatch threshold instead of an absolute deadline, define order-preserving contiguous-segment eviction, snapshot request shape and ad-unit array order at enqueue, define the payload projection with a reserved adapter allowance and a 413 re-dispatch-solo-once fallback, and pass through undefined cancelled-auction values with handler throws reported by async rethrow rather than facade rejection. Scope Lever B approval to every jurisdiction the deployed config serves, pin the NetLog protocol, and narrow the invariant to no request HEADERS/DATA frames. Strengthen Lever C discovery with server-side single-flight preference, no completed-response reuse (the adapter re-stamps ttl 300), a typed direct-path result, and input-ownership tables, and remove the dangling Phase 2 reference. Add the adapter activation matrix (Fastly-only pilot), the guardrail source table with a new coalesced-count signal, drain over a 48h document lifetime, randomized paired methodology with power-based sample sizes, and a real latency-benefit gate for B.
Replace the unimplementable post-response 413 replay with an authoritative size bound at the adapter buildRequests seam (constituent boundaries carried in; over-limit merged bodies split into multiple transport descriptors within one auction, preserving the single event stream). Replace the global async rethrow with a synchronous first-error throw after all handlers so Prebid's existing catch path handles it. Define the telemetry wire path as a top-level constituentCallCount body field through endpoint, observation context, event row, and a schema-first Tinybird column. Bound drain with a 24h injected-config lifetime and shim self-disable. Add the Fastly single-flight discovery exit criterion, extend the navigation-scoped reservation to both architectures, and specify the pending-only state machine with settled-result discard and cancellation honesty. Require a traffic-weighted browser matrix and durable approval artifact for Lever B with HTTP/1 write predicates and a versioned parser. Replace the circular eligible-burst denominator with candidate-burst coverage plus a 95 percent merge-success health gate, add a 30ms minimum worthwhile effect with named paired estimators, correct the Spin activation row, use a side-effect-free size estimator, add the segment-failure continuation test, and soften byte-for-byte wording.
Narrow the no-oversized-request guarantee to multi-constituent descriptors with an explicit singleton rule, key constituent boundaries by an internal auction ID read from the auction-scoped bidderRequest with a no-marker-leak test, and sanitize EIDs into plain data once per auction with unsanitizable EIDs making the body unsplittable. Replace constituentCallCount with a coalesced group/size/part object counted by distinct group, drop malformed values as absent, and name the auction_coalescing_daily rollup as the guardrail. Subordinate Lever C's pending-discard rule to one-consumer arbitration so the public requestAds render cannot be silently dropped, add the client-side real-artifact feasibility gate and the server-side side-effect-free prepare+fingerprint exit criterion, and require an outcome transition table. Block Lever B until every engine above 5 percent traffic is verified. Require the one-sided lower bound to exceed the 30ms effect, use a paired bootstrap quantile estimator for p95, define reuse denominators, split the causal harness from non-causal production guardrails with a fixed day-7 rule and daily batches, and make drain additive with a monotonic document lifetime and pipeline-liveness check.
Replace the unsplittable-EID rule with once-per-auction sanitization plus a per-constituent descriptor fallback when sanitization throws, noting the existing collector already re-reads accessor-bearing EIDs. Redefine Lever C's invariant as one provider execution permitting an authenticated side-effect-free join/claim, restrict sharing to a private fetch-only producer with exactly-once consumption, and give the reservation key a versioned keyed-digest contract that never appears raw in logs, URLs, or telemetry. Split Lever B's gates: privacy conformance on every engine family implementing the hint with a terminal observation horizon over all hint-attributed connections, performance acceptance on engines above 5 percent. Make retention pre-enable-only with holds as rollback-only guardrails read at day 9 for reporting maturity, unify the coalescing lifetime on the monotonic document-age contract, define boundary-map cleanup on consume/failure/settlement with a bounded map, require observed multi-POP propagation before drain clocks start, enforce approval expiry through gam_preconnect_approval_id/valid_until with automatic disable, tighten the gates to one-sided lower bounds and ratio-scale non-inferiority with BCa intervals and conservative indeterminate handling, bound the coalesced schema with cross-field validation, and add split-response aggregation, partial-413, deployment-skew fixtures, the per-engine matrix, operator-facing activation docs, and Lever C's consumption telemetry and go/no-go threshold.
Never evict live boundary entries — a full map dispatches new calls solo. Replace adapter-time EID sanitization with one raw-to-plain snapshot at collection reused by transport, split descriptors, bookkeeping, and cookie sync, with an explicit drop-and-count policy for non-serializable entries, retiring the per-constituent fallback. Make the drain bound hold end to end: config injection implies the 60s cache cap (cache-retaining response classes carry no config), document lifetime uses dual monotonic/wall-clock expiry with pageshow/visibilitychange resume guards, and propagation is observed across the served POP scope. Require a provider-free signed preflight token (input digest, navigation nonce, expiry; replay/mismatch/ navigation/expiry tests) for Lever C's client option, passive attach-rate measurement before any producer, admitted/rendered terminal outcomes, hard /auction and provider-execution non-regression gates, a keepalive 64 KiB transport gate, and blocking provider-log redaction. Turn Lever B's conformance gate into a finite product/OS/WebView matrix with an explicitly risk-accepted tail, bind emission to a signed approval manifest validated for hash, scope, and expiry, and record pre-consent DNS/TCP/TLS disclosure in the manifest. Use Wilson bounds for proportions with BCa reserved for paired continuous metrics, define no-render scoring, fix live control limits with minimum volumes and stale-data actions, constrain coalesced metadata to part <= parts <= size <= 4 with group consistency and a counted invalid-metadata signal, and surface the merged requestBids call visibility in Phase 0 and the real-artifact suite.
Store constituent boundaries as code-to-membership sets partitioned in buildRequests, with unexpected or duplicate codes dispatching one unsplit descriptor and a post-handoff mutation test. Scope the EID snapshot to merged auctions with its own auction-keyed lifetime that outlives the boundary record, independent snapshots for overlapping reverse-settling auctions, an unchanged window-0 path, and a data-property walk that never invokes custom toJSON. Enforce the 24h lifetime fail-closed on every wrapped call and flush via monotonic age, elapsed wall time from receipt, and backward-jump disable, dropping the skew allowance. Make the Lever B approval an embedded JCS-canonical Ed25519-signed envelope with pinned rotating keys, a configuration digest computed at load, and per-emission expiry rechecks; stop emission at valid_until minus propagation and freshness so cached HTML cannot outlive approval. Extend the injection cache bound to CDN directives, Expires, and validators so an origin 304 cannot revive flag-carrying HTML. Bind Lever C's producer and joiner both to the preflight token, name the pending-join owner for single use, require pure fingerprints in both architectures, drop keepalive outright given the aggregate quota, widen the privacy audit to every provider and mediator including APS trace logs and client-visible debug bodies, and ban linkable stable hashes. Give live rollback rules degradation polarity with Bonferroni-split looks, restrict daily batches to feasible metrics with p95 on the accumulated pool, define the numeric no-render censoring value and the paired count-ratio estimator for Lever A's drop gate. Make the matrix cell product-OS-version-consent, preserve the full hint-transport graph before sanitization, add the navigation-keyed document span so the passive gate is computable, include coordination overhead in the load gate, and add map-capacity-at-flush, wrapper-order, invalid-metadata propagation, and group-consistency tests with a baseline-compared invalid signal that cannot hold drain open.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Design spec for three Trusted-Server-side ad-latency levers on client-side-auction properties (SSAT off), grounded in an instrumented 3-run baseline collected against a local Trusted Server proxying the production autoblog origin (consent pre-seeded, reader-style scrolling).
Baseline findings the spec addresses:
window.load(~3.3 s) althoughDOMContentLoadedfires at ~2.1 s — only the DOM application needs the React-JS bundle hash recomputed on every HTML response instead of cached #418-safe post-load defer, not the auction fetchrequestBidsbursts (calls observed 1 ms apart) each pay a full/auctionround trip plus ~865 ms median server auction timesecurepubads.g.doubleclick.netProposed levers (spec only — no implementation in this PR):
request_bids_coalesce_ms(opt-in, default 0) — coalescing window in the tsjsrequestBidswrapper with a strict merge-safety rulesecurepubadspreconnect hint from the GPT integration head inserts (unconditional)prefetch_first_refresh_auction(opt-in, default false) — dispatch the first refresh auction fetch at DOMContentLoaded, apply results at the unchanged post-load + double-rAF pointBoth flags default off and serialize only at non-default values, following the existing config-blob rollback discipline.