Your measurement pipeline loses events at peak traffic and the attribution numbers never agree with the bidder logs. Those are two failures behind one symptom: events that never arrived, at a seam nobody counted, and events that arrived and were counted under a different rule. This is how the seams, the keys, the late window and the reconciliation bridge are built.
An event pipeline that loses events at peak traffic and never reconciles with the bidder logs can be two failures behind one symptom. One is transport: events created and never landed, at a seam nobody counted. The other is definitional: events that landed and were counted under a different rule than the auction record.
The usual framing - the pipeline drops events, so rebuild the pipeline - fixes at most one of them. What follows separates the two and says what a reconciliation produces, which is not equality. The defaults quoted below are Kafka on the transport side and ClickHouse on the storage side.
The short answer is structural: one identifier per auction carried end to end, a counter on both sides of every hop, and a reconciliation over a window that has already closed. What amBrain can substantiate publicly is the AdTech work: DSP development, real-time bidding platforms, and ad exchange engineering. In any RTB stack that is the side the bid, win and impression records come from. The pipeline below is described from the mechanics of the problem, not from a case of ours.
The first failure is loss: the event was created and never arrived, at a specific hop, for a specific reason. A beacon that never left the page, an edge restarted mid-deploy, a producer buffer that filled, a consumer that committed its offset before processing.
The second is not a failure at all. The measurement side counts a client-initiated event; the bidder records a server-side auction outcome. One is an award, the other an observation of what happened to it. The MRC measurement guidelines treat pre-fetch, pre-render and auto-refresh as separate things to detect and disclose: a counting rule, not a transport fault. Telling the two apart costs one query and some patience.
Until that curve exists, both sides of the argument are opinions. Afterwards its shape decides which half of this article applies, and the halves are not exclusive.
There are seven places where an ad event is created and then quietly stops existing, plus one setting that looks like a guarantee and is not.
The rule is a reporting rule, not an engineering one: a loss is attributed to a named seam, or not attributed at all. Two alarms keep the quiet seams visible - consumer lag measured in time against retention, and a reset policy that fails rather than jumps.
A deduplication key is not a convenient primary key chosen at the destination. It is assigned upstream of every retry, at auction time or when the event is created, and the receiver never invents one. Arrival timestamps stay out: a retry carries a new arrival time and a new key.
The key has to be identical across every attempt, which decides its parts: the exchange or seat, the auction identifier, the impression identifier and the event type. Partition the topic on that key, so retries land together and per-key ordering survives them. That instruction is for the transport only: the same word applied to the columnar store gives one partition per event, and the insert dies on the per-block limit. Partition storage by time, order by the key.
So the working shape is at-least-once transport with idempotent keys. Deduplication at write time keeps the storage bill sane; deduplication at read time is what makes the number correct. Merges never combine parts from different partitions, so a duplicate landing in the next partition is resolved only when a query asks.
Under overload a system has three options: slow the producer down, shed with a counter, or lose quietly. Only the third is unacceptable, and it is the default behaviour of code that was never asked the question. Loss at peak is a queue that grew until memory ran out, or an acknowledgement issued before durability.
A drop with a labelled counter is a known quantity that can be reconciled later. A drop without one is not lost data, it is a lost number.
The OpenRTB implementation guidance states it directly: the sequence from ad request through the auction to rendering and billing is fundamentally not transactional. Too many parties sit between the two counts.
Delay is expected rather than exceptional. The bid request can carry an impression expiry and the bid the delay the bidder tolerates, and the same guidance gives rules of thumb running from the order of a minute for web to far longer for cached in-app formats and stitched video.
Neither field is a contract. The guidance says plainly that a billing notice arriving later than the expiry a bidder declared may still be billable - a policy discussion between bidder and exchange rather than something the protocol imposes.
An event that arrives after the window is not a defect in the pipeline, it is a property of the medium. The only real choice is whether the number moves in public while the window is open, or moves in private afterwards.
The identifiers the exchange substitutes into notice and tracking URLs are the auction identifier from the bid request, the impression identifier, and, where the bidder minted one, the bid identifier. None of the three is a key on its own.
The specification calls the auction identifier exchange unique, not globally unique: two exchanges can hand you the same string on the same day. The impression identifier is unique only inside its own bid request, often the literal 1. The bid identifier is optional.
The key that holds is the composite: the exchange or seat you transacted through, plus the auction identifier, plus the impression identifier. Mint it on the bidder side at auction time, and treat anything shorter as a prefix rather than a key.
If those macros are absent from the beacons, event-level reconciliation is impossible, and what remains is matching on time, placement and creative. What you can build instead is a bridge of six counts, each naming its reason for differing from the step above.
A stable ratio between steps is the goal and an unexplained move is the alarm. Inside one system the hop ratios belong at one, and any excursion is the signal. Across the auction-to-measurement boundary, one is the suspicious reading.
The same counters, read as ratios, turn the question into arithmetic: accepted over sent, produced over accepted, consumed over produced, inserted over consumed. Four ratios on one chart say where the events went before anyone opens a log. Add a producer-side sequence number per source and partition, and a hole becomes evidence rather than a suspicion.
One question the bridge does not answer and a contract does: which of these numbers you pay on. The seller books revenue on its own billable event, the buyer paces on its own, and the guidance treats a persistent gap as a support conversation between the parties.
Decide in advance which count is the system of record for spend, and at what gap a report note becomes a ticket with the exchange. The work above buys attribution of loss, honest duplicates and a reconciliation explained line by line. It does not buy the following.
A pipeline that can answer those disclosure questions has an integrity story. One that cannot has an opinion, and an opinion is what gets argued about at the end of a quarter.
The failure worth designing against is not the missing hour that starts an investigation. It is the quiet version: a seam that sheds without a counter, an insert acknowledged before it was durable, and a reconciliation over a window still open.
What amBrain can substantiate publicly: amBrain is a Yerevan, Armenia software engineering company building low latency trading platforms, matching engines, and real-time bidding systems in Rust. amBrain has been building software since 2019. Rebuilding a measurement pipeline is not work described here. If the numbers stop reconciling on the bidder and exchange side - the bid, win and impression records themselves - that is the conversation worth having, and it starts with the settling curve rather than with a rebuild.
Bring your current architecture and the failure mode that worries you, and we will go through it together in half an hour.