Add bidder param zone overrides and per-provider auction details#347
Add bidder param zone overrides and per-provider auction details#347ChristianPavilonis wants to merge 9 commits intomainfrom
Conversation
f6406da to
3f6c9e7
Compare
7fade76 to
5d90d1f
Compare
…d returnallbidstatus field
Adds ProviderSummary struct to expose bid counts and bidder names (e.g. kargo, pubmatic) from each auction provider. Includes provider-specific metadata via AuctionResponse.metadata field for future integration data. Response now includes provider_details array in ext.orchestrator with one summary per provider showing status, bid count, unique bidders, and response time.
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
This PR adds useful Prebid zone-based bidder parameter overrides and richer per-provider auction details in ext.orchestrator. The overall direction is solid, and CI is green.
👍 Highlights
- The zone override path is well-covered across Rust and JS tests, including stale-zone cleanup behavior in the adapter shim.
- Adding per-provider diagnostics (
provider_details) is a strong observability improvement for auction debugging.
Findings
🤔 Medium (P2)
ProviderSummary.metadatacurrently serializes withskip_serializing_if = "HashMap::is_empty"but deserialization still requires the field. This can fail round-trip deserialization for valid payloads that omit empty metadata.
⛏ Low (P3)
parse_responsenow mapsext.responsetimemillisandext.errorsintoAuctionResponse.metadata, but there is no direct regression test for this path.
CI Status
- fmt: PASS
- clippy: PASS (Analyze (rust) check passed)
- rust tests: PASS
- js tests: PASS
| /// Response time in milliseconds. | ||
| pub time_ms: u64, | ||
| /// Provider-specific metadata (from [`AuctionResponse::metadata`]). | ||
| #[serde(skip_serializing_if = "HashMap::is_empty")] |
There was a problem hiding this comment.
🤔 Serialization/deserialization mismatch: metadata is skipped when empty (skip_serializing_if), but deserialization still requires field presence. Round-tripping JSON with omitted empty metadata can fail with missing field metadata.
Fix: add #[serde(default, skip_serializing_if = "HashMap::is_empty")] on this field. Consider #[serde(default)] for OrchestratorExt.provider_details as well for backward-compatible reads.
| // those that returned no bids, making it the canonical source for | ||
| // "who was in the auction." | ||
| let ext = response_json.get("ext"); | ||
| if let Some(rtm) = ext.and_then(|e| e.get("responsetimemillis")) { |
There was a problem hiding this comment.
⛏ Missing regression test: this path now copies ext.responsetimemillis and ext.errors into AuctionResponse.metadata, but there is no direct unit test for that parsing behavior. A regression here would silently drop diagnostics.
Fix: add a parse_response test that includes both fields and asserts they are preserved in auction_response.metadata.
Summary
bid_param_zone_overrides). Publishers can configure different placement IDs per ad zone (e.g., header vs sidebar vs in-content) via TOML. The JS adapter reads the zone frommediaTypes.banner.nameon each ad unit and sends it to the server, which shallow-merges the matching override into the bidder params before dispatching to Prebid Server.provider_detailsarray to the/auctionresponse atext.orchestrator. Each entry is aProviderSummarywith provider name, bid status, bid count, unique bidder/seat names, response time, and provider-specific metadata. For Prebid, the metadata includesresponsetimemillis(per-bidder timing for all invited bidders, not just those that bid) anderrorsfrom the Prebid Server response ext — making it easy to see which bidders were in the auction stream but returned no bids.Feature 1: Bidder Param Zone Overrides
The JS adapter reads
mediaTypes.banner.name(a non-standard Prebid.js field used as a temporary workaround) and includes it as azonefield in thetrustedServerbid params. On the server side,bid_param_zone_overridesis a nested map ofbidder → zone → paramsthat is shallow-merged into the incoming bidder params when a match is found.Config example:
Behavior:
Feature 2: Per-Provider Auction Response Details
The
/auctionresponse now includes aprovider_detailsarray inext.orchestrator:{ "ext": { "orchestrator": { "strategy": "parallel_only", "providers": 1, "total_bids": 2, "time_ms": 145, "provider_details": [ { "name": "prebid", "status": "success", "bid_count": 2, "bidders": ["ix", "kargo"], "time_ms": 142, "metadata": { "responsetimemillis": { "appnexus": 0, "ix": 120, "kargo": 98, "openx": 0, "rubicon": 0 }, "errors": { "openx": [{"code": 1, "message": "timeout"}] } } } ] } } }Note how
responsetimemillisincludes entries for bidders likeappnexusandrubiconthat returned 0ms (no bid), whilebiddersonly lists those that actually bid (ix,kargo). Theerrorsobject surfaces Prebid Server-reported errors per bidder.Changes
crates/common/src/integrations/prebid.rsbid_param_zone_overridesconfig field, zone extraction fromtrustedServerparams, shallow-merge logic,responsetimemillis/errorsmetadata extraction, new tests and test helperscrates/common/src/auction/types.rsProviderSummarystruct withFrom<&AuctionResponse>impl; addprovider_detailsfield toOrchestratorExtcrates/common/src/auction/formats.rsprovider_detailsvec from orchestration result and include in responsecrates/common/src/openrtb.rsprovider_details: vec![]crates/js/lib/src/integrations/prebid/index.tsmediaTypes.banner.nameas zone, includezonekey in trustedServer params, handle stale zone cleanupcrates/js/lib/test/integrations/prebid/index.test.tsdocs/guide/integrations/prebid.mdbid_param_zone_overridesconfig, behavior, and exampletrusted-server.tomlTest plan
cargo test --workspacecargo clippy --all-targets --all-features -- -D warningscargo fmt --all -- --checkcd crates/js/lib && npx vitest runcd crates/js/lib && npm run formatcd docs && npm run formatcargo build --bin trusted-server-fastly --release --target wasm32-wasip1fastly compute serveChecklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!)