AdTechSep 10, 202610 Min. Lesezeit

Ad Measurement verliert Events im Peak: Nahtstellen, doppelte Schlüssel und der Join mit dem Bidder

Event-PipelinesAd MeasurementAttributionDatenintegrität
Bild konnte nicht geladen werden

Ihre Messpipeline verliert Events bei Spitzenlast, und die Attributionszahlen stimmen nie mit den Bidder-Logs überein. Das sind zwei Fehler hinter einem Symptom: Events, die nie ankamen, an einer Nahtstelle, die niemand gezählt hat, und Events, die ankamen und nach einer anderen Regel gezählt wurden. Hier steht, wie Nahtstellen, Schlüssel, Late-Fenster und Abgleichsbrücke gebaut werden.

Eine Event-Pipeline, die bei Spitzenlast Events verliert und sich nie mit den Bidder-Logs abgleichen lässt, kann zwei Fehler hinter einem Symptom sein. Der eine ist Transport: Events, die erzeugt wurden und nie ankamen, an einer Nahtstelle, die niemand gezählt hat. Der andere ist definitorisch: Events, die ankamen und nach einer anderen Regel gezählt wurden als der Auktionsdatensatz.

Die übliche Erzählung - die Pipeline verliert Events, also baut man die Pipeline neu - behebt höchstens eines davon. Was folgt, trennt die beiden und sagt, was ein Abgleich liefert, und das ist keine Gleichheit. Die unten zitierten Defaults sind Kafka auf der Transportseite und ClickHouse auf der Storage-Seite.

Die kurze Antwort ist strukturell: ein Identifier pro Auktion, durchgängig mitgeführt, ein Counter auf beiden Seiten jedes Hops und ein Abgleich über ein bereits geschlossenes Fenster. Was amBrain öffentlich belegen kann, ist die AdTech-Arbeit: DSP-Entwicklung, Real-Time-Bidding-Plattformen und Ad-Exchange-Engineering. In jedem RTB-Stack ist das die Seite, von der die Bid-, Win- und Impression-Records kommen. Die Pipeline unten ist aus der Mechanik des Problems beschrieben, nicht aus einem unserer Fälle.

Eine Query unterscheidet ein verlorenes Event von einem späten

Der erste Fehler ist Verlust: Das Event wurde erzeugt und kam nie an, an einem bestimmten Hop, aus einem bestimmten Grund. Ein Beacon, der die Seite nie verlassen hat, eine Edge, die mitten im Deploy neu startete, ein Producer-Puffer, der voll lief, ein Consumer, der seinen Offset vor der Verarbeitung committet hat.

Der zweite ist überhaupt kein Fehler. Die Messseite zählt ein clientseitig ausgelöstes Event, der Bidder hält ein serverseitiges Auktionsergebnis fest. Das eine ist ein Zuschlag, das andere eine Beobachtung dessen, was damit geschah. Die MRC-Messrichtlinien behandeln pre-fetch, pre-render und auto-refresh als eigene Dinge, die zu erkennen und offenzulegen sind: eine Zählregel, kein Transportfehler. Die beiden auseinanderzuhalten kostet eine Query und etwas Geduld.

  • Fahren Sie dasselbe Event-Time-Fenster eine Stunde, sechs Stunden und einen ganzen Tag nach dem Zeitraum, den es abdeckt, erneut
  • Ein Defizit, das mit jedem Lauf schrumpft, heißt: Die Events waren spät, nicht verloren, und der Transport ist in Ordnung
  • Zählen Sie distinkte Deduplizierungsschlüssel, nicht Zeilen: at-least-once-Transport garantiert Wiederzustellungen, und eine zeilenbasierte Kurve verbirgt eine Überzählung
  • Ein Defizit, das flach bleibt, heißt: Die Events sind weg, und die Frage ist, welche Nahtstelle
  • Der Test braucht einen Event-Time-Zeitstempel, einen Identifier, der Retries übersteht, und eine Retention, die lang genug ist, um das Fenster erneut zu fahren
  • Veröffentlichen Sie die Settling-Kurve als Chart, je Event-Typ, neben der Zahl, die sie erklärt

Solange diese Kurve nicht existiert, sind beide Seiten der Diskussion Meinungen. Danach entscheidet ihre Form, welche Hälfte dieses Artikels gilt, und die Hälften schließen sich nicht aus.

Events verschwinden an benannten Nahtstellen, und einer ungezählten Nahtstelle kann man nichts anlasten

Es gibt sieben Stellen, an denen ein Ad-Event erzeugt wird und dann still aufhört zu existieren, dazu eine Einstellung, die wie eine Garantie aussieht und keine ist.

  • Client-Erfassung: Der Beacon feuert, aber das Dokument wird vorher entladen, oder das Creative war gecacht, pre-fetched oder auto-refreshed und zählt, was der Auktionsdatensatz nicht zählt
  • Edge-Ingest: Verbindungslimits, erschöpfte keep-alive-Verbindungen, Neustarts während Deploys und die gefährliche Variante - Erfolg wird zurückgegeben, bevor das Event durable ist
  • Producer-Puffer: Der Client blockiert für eine begrenzte Zeit und wirft dann einen Fehler, und Code, der den Fehler fängt und nichts zählt, ist die Stelle, an der Daten sterben
  • Broker-Durability: Ist kein Acknowledgement verlangt, garantiert nichts, dass der Record angekommen ist; mit dem Leader allein ist er verloren, wenn dieser Leader ausfällt, bevor die Follower repliziert haben
  • Acknowledgement von allen Replicas ist keine Durability: Es wartet auf das aktuelle In-Sync-Set, dessen Mindestgröße standardmäßig eins ist, sodass eine Spitze, die Follower zurückfallen lässt, allein auf dem Leader committet
  • Consumer: Den Offset vor der Verarbeitung zu committen ist at-most-once, und das ist ein Default und keine Entscheidung - der Client committet auf einem Timer, solange das nicht abgeschaltet wurde
  • Retention-Überlauf: Ein Consumer, der über das Retention-Fenster hinaus zurückfällt, findet seinen nächsten Offset gelöscht vor, und die Standard-Reset-Policy lässt ihn an den Kopf des Logs springen
  • Laden in den Columnar Store: Fire-and-forget-Inserts bestätigen, sobald gepuffert, und abhängige Materialized Views deduplizieren über eine eigene Einstellung - hier trennen sich Rohtabelle und Report

Die Regel ist eine Reporting-Regel, keine Engineering-Regel: Ein Verlust wird einer benannten Nahtstelle zugeordnet oder gar nicht. Zwei Alarme halten die leisen Nahtstellen sichtbar - Consumer Lag, in Zeit gegen die Retention gemessen, und eine Reset-Policy, die fehlschlägt, statt zu springen.

Deduplizierung braucht einen Schlüssel, der vor dem ersten Retry existiert

Ein Deduplizierungsschlüssel ist kein bequemer Primärschlüssel, den man am Ziel wählt. Er wird vor jedem Retry vergeben, zur Auktionszeit oder bei der Erzeugung des Events, und der Empfänger erfindet nie einen. Ankunftszeitstempel bleiben draußen: Ein Retry trägt eine neue Ankunftszeit und damit einen neuen Schlüssel.

Der Schlüssel muss über jeden Versuch hinweg identisch sein, und das entscheidet über seine Teile: die Exchange oder das Seat, der Auktions-Identifier, der Impression-Identifier und der Event-Typ. Partitionieren Sie das Topic auf diesen Schlüssel, damit Retries zusammen landen und die Reihenfolge je Schlüssel sie übersteht. Diese Anweisung gilt nur für den Transport: Dasselbe Wort auf den Columnar Store angewandt ergibt eine Partition pro Event, und der Insert stirbt am Limit pro Block. Partitionieren Sie den Storage nach Zeit und ordnen Sie nach dem Schlüssel.

  • Der idempotente Producer entfernt Duplikate aus Producer-Retries innerhalb einer Session, und Kafka aktiviert ihn seit 3.0 standardmäßig, zusammen mit Acknowledgement von allen Replicas
  • Dieser Default ist an Bedingungen geknüpft: Eine widersprüchliche Einstellung aus einer älteren Konfiguration schaltet die Idempotenz still ab, fragen Sie also den laufenden Prozess, was er hat
  • Ein Duplikat auf Anwendungsebene kann er nicht sehen: einen abgestürzten Prozess, der erneut gesendet hat, einen zweimal gefeuerten Beacon, einen Operator, der einen Ingest-Job erneut laufen lässt
  • Die Insert-Deduplizierung von ClickHouse hasht den Blockinhalt, also schickt ein Consumer, der nach einem Rebalance neu batcht, dieselben Zeilen in neuer Form, und der Hash greift nicht
  • Das Fenster ist in Blöcken und in Zeit begrenzt, und auf nicht replizierten Tabellen steht es standardmäßig auf null, also aus; ein Insert-Token beseitigt diese Abhängigkeit
  • Exactly-once innerhalb des Logs deckt consume-transform-produce ab, und der Hop in eine analytische Datenbank liegt außerhalb dieser Grenze, was der Transport auch verspricht

Die tragfähige Form ist also at-least-once-Transport mit idempotenten Schlüsseln. Deduplizierung beim Schreiben hält die Speicherkosten im Rahmen, Deduplizierung beim Lesen macht die Zahl richtig. Merges kombinieren nie Parts aus verschiedenen Partitionen, ein Duplikat, das in der nächsten Partition landet, wird also erst aufgelöst, wenn eine Query danach fragt.

Backpressure entscheidet, ob ein Verlust eine Zahl ist oder ein Gerücht

Unter Überlast hat ein System drei Optionen: den Producer bremsen, mit einem Counter abwerfen oder still verlieren. Nur die dritte ist inakzeptabel, und sie ist das Standardverhalten von Code, dem die Frage nie gestellt wurde. Verlust im Peak ist eine Queue, die wuchs, bis der Speicher ausging, oder ein Acknowledgement, das vor der Durability erteilt wurde.

  • Begrenzte Queues an jedem Hop, mit expliziter Ablehnung statt Wachstum. Eine unbegrenzte Queue verlagert den Verlust in Speicherdruck und einen Neustart
  • Producer-Blockierzeit und Puffergröße sind Kapazitätsentscheidungen: Dimensionieren Sie sie nach dem gemessenen Peak und alarmieren Sie auf die blockiert verbrachte Zeit
  • Werfen Sie nach Klasse ab, nicht zufällig: Impression- und abrechenbare Events überleben, Diagnose-Events gehen zuerst, und jedes abgeworfene Event erhöht einen beschrifteten Counter
  • Consumer Lag ist sichtbar gemachte Backpressure. Alarmieren Sie auf das Alter des ältesten unverarbeiteten Events und darauf, wie schnell sich der Lag ändert

Ein Drop mit einem beschrifteten Counter ist eine bekannte Größe, die sich später abgleichen lässt. Ein Drop ohne Counter ist nicht verlorene Daten, sondern eine verlorene Zahl.

Späte Ankunft ist strukturell, und die Hälfte der Abweichung ist eine Kalenderfrage

Die OpenRTB-Implementierungs-Guidance sagt es direkt: Die Abfolge von der Ad Request über die Auktion bis zu Rendering und Abrechnung ist grundsätzlich nicht transaktional. Zwischen den beiden Zählwerten sitzen zu viele Parteien.

Verzögerung ist der erwartete Fall, nicht die Ausnahme. Die Bid Request kann einen Ablauf für die Impression tragen und das Gebot die Verzögerung, die der Bidder toleriert, und dieselbe Guidance gibt Faustregeln von der Größenordnung einer Minute für Web bis zu deutlich mehr für gecachte In-App-Formate und stitched Video.

Keines der beiden Felder ist ein Vertrag. Die Guidance sagt klar, dass eine Billing Notice, die später eintrifft als der vom Bidder erklärte Ablauf, trotzdem abrechenbar sein kann - eine Policy-Diskussion zwischen Bidder und Exchange und nichts, was das Protokoll vorschreibt.

  • Pro Event existieren drei Zeitstempel, und genau einer steuert das Fenster: die Geräteuhr, nicht vertrauenswürdig; die Empfangszeit an der Edge, spät; die Auktionszeit, maßgeblich
  • Einen vierten gibt es dort, wo die Exchange ihn liefert: das Makro mit dem Moment, in dem die Impression erfüllt wurde; fehlt es, geht die Spezifikation davon aus, dass die Notice innerhalb von Sekunden folgte
  • Ein Watermark erklärt, dass die Event-Zeit einen Punkt erreicht hat und keine früheren Elemente mehr erwartet werden, die erlaubte Verspätung ist also ein Parameter, den Sie wählen
  • Veröffentlichen Sie das Late-Fenster je Event-Typ zusammen mit der Restatement-Policy: Zahlen bewegen sich, solange es offen ist, frieren dann ein, und Bewegungen werden protokolliert
  • Zählen Sie das späte Event und markieren Sie es als spät, denn es zu verwerfen heißt, Spend zu verwerfen, der Ihnen berechnet wurde

Ein Event, das nach dem Fenster ankommt, ist kein Defekt der Pipeline, sondern eine Eigenschaft des Mediums. Die einzige echte Wahl ist, ob sich die Zahl öffentlich bewegt, solange das Fenster offen ist, oder danach im Verborgenen.

Der Abgleich joint über die Identifier, die das Protokoll ohnehin trägt

Die Identifier, die die Exchange in Notice- und Tracking-URLs einsetzt, sind der Auktions-Identifier aus der Bid Request, der Impression-Identifier und, wo der Bidder einen erzeugt hat, der Bid-Identifier. Keiner der drei ist für sich allein ein Schlüssel.

Die Spezifikation nennt den Auktions-Identifier exchange unique, nicht global eindeutig: Zwei Exchanges können Ihnen am selben Tag dieselbe Zeichenkette geben. Der Impression-Identifier ist nur innerhalb seiner eigenen Bid Request eindeutig, oft die wörtliche 1. Der Bid-Identifier ist optional.

Der Schlüssel, der hält, ist der zusammengesetzte: die Exchange oder das Seat, über die Sie gehandelt haben, plus der Auktions-Identifier, plus der Impression-Identifier. Erzeugen Sie ihn auf der Bidder-Seite zur Auktionszeit und behandeln Sie alles Kürzere als Präfix und nicht als Schlüssel.

Fehlen diese Makros in den Beacons, ist ein Abgleich auf Event-Ebene unmöglich, und es bleibt nur das Matching über Zeit, Placement und Creative. Was Sie stattdessen bauen können, ist eine Brücke aus sechs Zählwerten, von denen jeder den Grund benennt, warum er vom Schritt darüber abweicht.

  • Auktionsgewinne, aus dem Bidder-Log - der einzige Zählwert, der vollständig Ihnen gehört
  • Win Notices, die die Exchange empfangen hat - die Differenz sind Notice-Verluste und Timeouts, und laut Spezifikation impliziert eine Win Notice nicht zwingend eine Auslieferung
  • Beacons, die an Ihrer Edge ankommen - die Differenz sind die Client-Erfassung und jede Nahtstelle darüber
  • Events nach der Deduplizierung - die Differenz sind Retries, und sie sollte von Woche zu Woche stabil sein
  • Events nach der Invalid-Traffic-Filterung - die Differenz ist eine Filterrate, die Sie veröffentlichen und nicht erst entdecken
  • Abrechenbare Events - die Differenz ist die Abrechnungsregel, und die Notice gehört auf die Serverseite, wo die Exchange den Umsatz verbucht

Ein stabiles Verhältnis zwischen den Schritten ist das Ziel, eine unerklärte Bewegung ist der Alarm. Innerhalb eines Systems gehören die Hop-Verhältnisse auf eins, und jede Abweichung ist das Signal. Über die Grenze von der Auktion zur Messung hinweg ist eins der verdächtige Wert.

Dieselben Counter, als Verhältnisse gelesen, machen aus der Frage Arithmetik: akzeptiert zu gesendet, produziert zu akzeptiert, konsumiert zu produziert, eingefügt zu konsumiert. Vier Verhältnisse in einem Chart sagen, wohin die Events gegangen sind, bevor jemand ein Log öffnet. Ergänzen Sie eine producerseitige Sequenznummer je Quelle und Partition, und aus einem Loch wird ein Beleg statt eines Verdachts.

Was eine neu gebaute Pipeline Ihnen nicht gibt

Eine Frage beantwortet die Brücke nicht, ein Vertrag schon: auf welche dieser Zahlen Sie zahlen. Der Verkäufer verbucht Umsatz auf sein eigenes abrechenbares Event, der Käufer steuert sein Pacing über seines, und die Guidance behandelt eine dauerhafte Lücke als Support-Gespräch zwischen den Parteien.

Entscheiden Sie vorab, welcher Zählwert das System of Record für Spend ist und ab welcher Abweichung aus einer Report-Notiz ein Ticket bei der Exchange wird. Die Arbeit oben erkauft Ihnen die Zuordnung von Verlusten, ehrliche Duplikate und einen Abgleich, der Zeile für Zeile erklärt ist. Sie erkauft Ihnen das Folgende nicht.

  • Sie macht die beiden Zählwerte nicht gleich: Beide Seiten zählen absichtlich unterschiedliche Events, und die Differenz wird erklärt, nie beseitigt
  • Sie holt keine Events zurück, die verworfen wurden, bevor es die Instrumentierung gab - die Settling-Kurve beginnt an dem Tag, an dem die Counter beginnen
  • Sie beseitigt das Restatement nicht: Gestern bewegt sich, solange das Late-Fenster offen ist, und ein Geschäft, das das nicht verträgt, braucht einen späteren Abschluss
  • Sie übersteht fehlende Makros nicht: Ohne die Auktions-Identifier in den Beacons erzeugt kein Storage-Design einen Join auf Event-Ebene
  • Sie macht gesampelte Daten nicht nachträglich joinbar, denn das Sampling entscheidet vor dem Schreiben der Zeile, welche Fragen beantwortbar bleiben
  • Sie ersetzt nicht die Offenlegungsliste, die ein Audit nach MRC-Art erwartet: Erfassungspunkt, Logging-Frequenz, Latenzschätzungen, Regeln für Inkonsistenzen

Eine Pipeline, die diese Offenlegungsfragen beantworten kann, hat eine Integritätsgeschichte. Eine, die es nicht kann, hat eine Meinung, und über Meinungen wird am Quartalsende gestritten.

Der Fehler, gegen den zu entwerfen sich lohnt, ist nicht die fehlende Stunde, die eine Untersuchung auslöst. Es ist die leise Variante: eine Nahtstelle, die ohne Counter abwirft, ein Insert, das vor seiner Durability bestätigt wurde, und ein Abgleich über ein noch offenes Fenster.

Was amBrain öffentlich belegen kann: amBrain ist ein Software-Engineering-Unternehmen aus Jerewan, Armenien, das Trading-Plattformen mit niedriger Latenz, Matching-Engines und Real-Time-Bidding-Systeme in Rust baut. amBrain baut seit 2019 Software. Der Neubau einer Messpipeline ist keine Arbeit, die hier beschrieben wird. Wenn die Zahlen auf der Bidder- und Exchange-Seite nicht mehr zusammenpassen - die Bid-, Win- und Impression-Records selbst -, dann ist das das Gespräch, das sich lohnt, und es beginnt mit der Settling-Kurve statt mit einem Neubau.

Liegt ein solcher Entwurf bei Ihnen auf dem Tisch?

Bringen Sie Ihre aktuelle Architektur und den Fehlerfall mit, der Sie beunruhigt - wir gehen ihn in einer halben Stunde gemeinsam durch.

Verwandte Artikel

Bild konnte nicht geladen werden
AdTech
Sep 10, 202610 Min. Lesezeit

Go-GC-Pausen in einem RTB-Bidder: Mark Assist, Deadlines und die Rust-Entscheidung

Beitrag lesen
Bild konnte nicht geladen werden
AdTech
Mar 5, 20267 Min. Lesezeit

Wie KI Programmatic Advertising 2026 verändert

Beitrag lesen
Bild konnte nicht geladen werden
AdTech
Feb 14, 20266 Min. Lesezeit

Privacy-First-Targeting: Ad Tech ohne Third-Party-Cookies bauen

Beitrag lesen