Skip to content

displayio: stamp bids with the impression id we requested - #132

Merged
zwl-williamzha merged 10 commits into
msp-2506from
fix-displayio-imp-id
Aug 6, 2026
Merged

displayio: stamp bids with the impression id we requested#132
zwl-williamzha merged 10 commits into
msp-2506from
fix-displayio-imp-id

Conversation

@zwl-williamzha

Copy link
Copy Markdown

What

MakeBids passed display.io's bids through untouched, keeping whatever impid the exchange returned. It now restamps them with the impression the request asked about, taken from RequestData.ImpIDs (which MakeRequests already records per fanned-out request).

Why

Display.io answers with impid: "1" instead of echoing the impression id we send, so its bids match no impression in the request. MSP's remove_tmax hook keeps only the bids sharing the top bid's ImpID, so display.io's bids sat in an imp group of their own and were silently deleted — or, whenever one of them held the top price, deleted every other seat's bids instead.

Prod, an 8-minute window: 112 display.io bids dropped this way across 6 placements, every one carrying imp_id: "1", the highest at $7.16. Example (the log line is from the observability added in ParticleMedia/msp#2981):

{"placementId":"msp-ios-article-inside-display-ctrl3","winning_seat":"vungle",
 "winning_imp":"09CACFCC-2924-4A68-85F8-53EF20BBDDDB","winning_price":3.31,
 "dropped_bids":[{"seat":"displayio","price":1.373804,"imp_id":"1"}],
 "num_dropped":1,"num_kept":4}

This is the same failure mode as ParticleMedia/msp#2982 (the Google adapter stamped the request id), reached by a different route: there we generated the wrong id ourselves, here the partner sends it and the adapter trusted it.

Multi-imp and the fallback

ImpIDs is per fanned-out request, so it stays correct when a multi-imp request is split. The single-imp fallback exists because the JSON test harness builds RequestData without ImpIDs (adapters/adapterstest/test_json.go ToRequestData copies only Method/Uri/Body) — without it the fixtures could not exercise this path at all. With several imps and no ImpIDs there is no way to tell which imp answered, so the bid is left untouched and the existing exemplary/multi-imp.json expectations still hold.

Tests

New supplemental/response-impid-mismatch.json reproduces the real response shape: mock response returns impid: "1", the expected bid carries the requested imp id. It fails without this change (-"impid": "1" / +"impid": "impId10111011101110111011"). Full adapters/displayio suite, go vet and gofmt all pass.

Follow-up

Merging this needs a submodule pointer bump in ParticleMedia/msp to take effect.

🤖 Generated with Claude Code

zwl-williamzha and others added 3 commits August 5, 2026 17:36
Display.io answers with impid "1" rather than echoing the impression id we send,
so its bids match no impression in the request. MakeBids passed the bid through
untouched, which left MSP's remove_tmax collapse — it keeps only the bids sharing
the top bid's ImpID — silently deleting them, or deleting every other seat's bids
whenever a display.io bid held the top price.

Prod, 8 minutes: 112 display.io bids dropped this way across 6 placements, every
one carrying imp_id "1", the highest at $7.16.

The imp comes from RequestData.ImpIDs, which MakeRequests already records per
fanned-out request, so it stays correct for multi-imp requests. The single-imp
fallback covers RequestData built without ImpIDs, as the JSON test harness does
(ToRequestData drops the field); with several imps there is no way to tell which
one answered, so the bid is left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
zwl-williamzha and others added 7 commits August 5, 2026 17:41
ToRequestData built RequestData from a fixture's expectedRequest but dropped
impIDs, even though the fixture declares the field and testMakeRequestsImpl
already asserts it. In production MakeBids receives the RequestData that the
adapter's own MakeRequests produced, ImpIDs included, so the harness was strictly
less faithful than the exchange.

Any adapter that resolves a bid's impression from RequestData.ImpIDs — displayio
now does, to repair responses that come back with impid "1" — was therefore
untestable through these fixtures: ImpIDs read empty and every expected impid
mismatched.

Full ./adapters/... suite passes. Only seedtag reads the field otherwise, for bid
slice capacity, and adocean tracks its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MSP sends displayio a single imp, so bidReq.Imp[0] is the impression the bid
answers and matching google.go here is right. Scoped to exactly one imp because
MakeRequests fans a multi-imp request out into one HTTP call per imp while
MakeBids is handed the whole request each time, so Imp[0] would be correct for
the first response and wrong for the rest — which the exemplary/multi-imp fixture
catches.

The empty check keeps that case as it was: with no imp id to trust, the bids are
left as they arrive rather than blanked, which would make prebid drop them for a
missing impid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Matches the shape of the google adapter: one imp per request, so the bid answers
bidReq.Imp[0] and is stamped with it.

Removes exemplary/multi-imp.json, which asserted per-imp stamping on a fan-out
that MSP never sends, and the supplemental fixture added earlier for the impid
"1" repair.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The harness change was only load-bearing while displayio resolved a bid's imp
from RequestData.ImpIDs. It now reads bidReq.Imp[0].ID, so nothing in the tree
consumes the field in MakeBids and this is unrelated scope for a displayio fix.
adapters/displayio and the full ./adapters/... suite pass without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…p requests

Restores exemplary/multi-imp.json and stamps only when the request carries
exactly one imp. MakeRequests fans a multi-imp request out into one HTTP call per
imp while MakeBids is handed the whole request each time, so Imp[0] is right for
the first response and wrong for the rest — the fixture asserts exactly that.

MSP sends displayio a single imp, so real traffic is always restamped; the empty
check leaves a multi-imp response as it arrives rather than mislabelling it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zwl-williamzha
zwl-williamzha merged commit 80a99ca into msp-2506 Aug 6, 2026
1 check passed
@zwl-williamzha
zwl-williamzha deleted the fix-displayio-imp-id branch August 6, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants