fix(analytics/prebid): accumulate all bidWon events before emitting witness#275
fix(analytics/prebid): accumulate all bidWon events before emitting witness#275
Conversation
…itness
Previously, the first `bidWon` event for an auctionId cleared the
pending timeout, emitted the witness, and deleted the auction from the
in-memory map. Every subsequent `bidWon` for the same auction hit the
"Missing auctionEnd event" guard and was silently dropped. On
multi-ad-unit pages only the first winning ad unit was captured.
Changes:
- `AuctionItem` gains a `bidWonEvents: any[]` accumulator field.
- `trackBidWon` no longer emits; it pushes the incoming event onto
`auction.bidWonEvents` and, when called with `missed=true`,
propagates the flag to `auction.missed` so the timeout callback
picks it up correctly.
- The `bidWinTimeout` setTimeout in `trackAuctionEnd` is now the sole
emit point. It reads `auction.bidWonEvents` and `auction.missed` at
fire time, calls `toWitness` with the full array, sets `bidWonAt`
only when at least one win was collected, and then removes the
auction from the map.
- `toWitness` second parameter changes from `bidWonEvent: any | null`
to `bidWonEvents: any[]`; `properties.bidWon` is now always an array
of `{bidderCode, adUnitCode, cpm}` objects (empty when no wins
arrived within the timeout window).
Tests updated:
- All `toWitness` call sites pass arrays.
- `trackBidWon` and `setHooks` tests use `jest.useFakeTimers()` and
`jest.runAllTimersAsync()` to trigger the deferred emit.
- New test verifies that two `bidWon` events for the same auction
produce exactly one witness call with a two-element `bidWon` array.
Quick review:Fix is correct: single emit point in Issues
Nits
TestsGood: fake timers replace flaky sleep, new multi-bidWon test covers the bug. Missing:
CoordinationWire format break ( VerdictApprove after doc refresh + |
- Fix bidWonAt semantics: stamp _receivedAt on each bidWon event at push time and use the earliest timestamp in the timeout payload, so bidWonAt reflects actual win time rather than auctionEnd + bidWinTimeout - Rename shadowed auction variable to storedAuction inside timeout closure - Simplify trackBidWon: remove log-only filteredEvent intermediate variable - Update stale JSDoc on trackBidWon and toWitness to reflect accumulation behaviour - Add tests: missed→optableLoaded:false, no-bidWon emits bidWon:[]/ bidWonAt:null, auction removed from map after timeout fires
174e445 to
7857162
Compare
mosherBT
left a comment
There was a problem hiding this comment.
10s timeout on this means that unless configured differently it will always wait 10s? I am worried we will lose impressions of people leaving the page in that time. I may be misunderstanding
| auctionEndTimeoutId: NodeJS.Timeout | null; | ||
| missed: boolean; | ||
| createdAt: Date; | ||
| bidWonEvents: any[]; |
There was a problem hiding this comment.
Does this need to be any? can we define the type
Hi @mosherBT your understanding is correct. A better alternative design would be to send a single |
|
Approving I think this is good to merge. The timeout I still think we should consider fixing. I wonder if there is prebid auction timeouts that we could leverage instead of a hard set default. Either way I think it is a fine step to have a timeout |
|
we should add an onpageunload event that flushes the cache before the user navigates away. |
|
@sergeydyachok-optable let's look at John's suggestion tomorrow please as a separate PR - it'd be a good enhancement to the SDK |
Summary
Fixes the data-loss bug where only the first
bidWonevent per Prebid auction was captured on multi-ad-unit pages (issue #69).Root cause:
trackBidWoncleared the pendingbidWinTimeout, emitted the witness immediately, and deleted the auction from the in-memory map. Every subsequentbidWonfor the sameauctionIdhit the"Missing 'auctionEnd' event"guard and was silently dropped.Fix:
trackBidWonnow only accumulates events; thebidWinTimeouttimer intrackAuctionEndis the sole emit point. After the timeout fires, all collectedbidWonevents are included in the witness payload as an array.Changes
lib/addons/prebid/analytics.tsAuctionIteminterface — addedbidWonEvents: any[]accumulator field.trackBidWon— stripped toauction.bidWonEvents.push(event)+auction.missed = truepropagation when called withmissed=true. No more timeout clear, no early emit, no map deletion.trackAuctionEndsetTimeout callback — readsauction.bidWonEventsandauction.missedat fire time (so late-arrivingmissed=truepropagation is respected), emits the witness with the full array, then removes the auction from the map.toWitness— second parameter changed frombidWonEvent: any | nulltobidWonEvents: any[];properties.bidWonis now always an array of{ bidderCode, adUnitCode, cpm }objects (empty array when nobidWonarrived within the timeout window).lib/addons/prebid/analytics.test.tstoWitness(…, null)call sites updated totoWitness(…, []).trackBidWondescribe block usesjest.useFakeTimers()/jest.runAllTimersAsync()to drive the deferred emit.setHooks"missed bidWon" test migrated to fake timers.trackBidWoncalls for the same auction produce exactly one witness call with a two-elementbidWonarray.bidWonEvents: []assertion to thetrackAuctionEndstored-auction test.Wire format change
properties.bidWonchanges from{ message, bidderCode, adUnitCode, cpm } | nulltoArray<{ bidderCode, adUnitCode, cpm }>. The companion backend change (handling the array and assigningwin=trueper entry) should be deployed first, per the coordination note in the issue.Test plan
bidWonaccumulation test passesPOST /witnesswithproperties.bidWon.length === 2