AdTechSep 10, 2026Lecture 10 min

La mesure publicitaire perd des événements au pic : raccords, clés dupliquées et jointure avec le bidder

Pipelines d'événementsMesure publicitaireAttributionIntégrité des données
Erreur de chargement de l'image

Votre pipeline de mesure perd des événements au pic de trafic et les chiffres d'attribution ne concordent jamais avec les logs du bidder. Ce sont deux pannes derrière un seul symptôme : des événements qui ne sont jamais arrivés, à un raccord que personne n'a compté, et des événements arrivés et comptés selon une règle différente. Voici comment se construisent les raccords, les clés, la fenêtre de retard et le pont de réconciliation.

Un pipeline d'événements qui perd des événements au pic de trafic et ne se réconcilie jamais avec les logs du bidder peut être deux pannes derrière un seul symptôme. La première est de transport : des événements créés et jamais arrivés, à un raccord que personne n'a compté. La seconde est définitionnelle : des événements arrivés et comptés selon une règle différente de celle de l'enregistrement d'enchère.

Le cadrage habituel - le pipeline perd des événements, donc reconstruisons le pipeline - en répare au plus un. Ce qui suit sépare les deux et dit ce que produit une réconciliation, ce qui n'est pas l'égalité. Les valeurs par défaut citées ci-dessous sont celles de Kafka côté transport et de ClickHouse côté stockage.

La réponse courte est structurelle : un identifiant par enchère porté de bout en bout, un compteur des deux côtés de chaque saut, et une réconciliation sur une fenêtre déjà close. Ce qu'amBrain peut étayer publiquement, c'est le travail AdTech : développement de DSP, plateformes d'enchères en temps réel et ingénierie d'ad exchange. Dans n'importe quelle stack RTB, c'est le côté d'où viennent les enregistrements de bid, de win et d'impression. Le pipeline ci-dessous est décrit à partir de la mécanique du problème, pas à partir d'un cas à nous.

Une requête distingue un événement perdu d'un événement en retard

La première panne est la perte : l'événement a été créé et n'est jamais arrivé, à un saut précis, pour une raison précise. Un beacon qui n'a jamais quitté la page, un edge redémarré en plein déploiement, un buffer de producteur qui s'est rempli, un consommateur qui a commité son offset avant le traitement.

La seconde n'est pas du tout une panne. Le côté mesure compte un événement initié par le client ; le bidder enregistre un résultat d'enchère côté serveur. L'un est une adjudication, l'autre une observation de ce qui lui est arrivé. Les lignes directrices de mesure du MRC traitent le pre-fetch, le pre-render et l'auto-refresh comme des éléments distincts à détecter et à divulguer : une règle de comptage, pas une panne de transport. Distinguer les deux coûte une requête et un peu de patience.

  • Rejouez la même fenêtre en temps d'événement une heure, six heures et un jour entier après la période qu'elle couvre
  • Un déficit qui diminue à chaque exécution signifie que les événements étaient en retard, pas perdus, et que le transport va bien
  • Comptez les clés de déduplication distinctes, pas les lignes : le transport at-least-once garantit des livraisons répétées, et une courbe fondée sur les lignes masque un surcomptage
  • Un déficit qui reste stable signifie que les événements ont disparu, et la question est de savoir à quel raccord
  • Le test a besoin d'un timestamp en temps d'événement, d'un identifiant qui survit aux retries, et d'une rétention assez longue pour rejouer la fenêtre
  • Publiez la courbe de stabilisation sous forme de graphique, par type d'événement, à côté du chiffre qu'elle explique

Tant que cette courbe n'existe pas, les deux camps de la discussion sont des avis. Ensuite, sa forme décide quelle moitié de cet article s'applique, et les deux moitiés ne s'excluent pas.

Les événements disparaissent à des raccords nommés, et un raccord non compté ne peut pas être mis en cause

Il y a sept endroits où un événement publicitaire est créé puis cesse discrètement d'exister, plus un réglage qui ressemble à une garantie et n'en est pas une.

  • Collecte côté client : le beacon se déclenche mais le document se décharge d'abord, ou bien la créative était en cache, pre-fetchée ou auto-refreshée et compte ce que l'enregistrement d'enchère ne compte pas
  • Ingestion à l'edge : limites de connexions, épuisement des keep-alive, redémarrages pendant les déploiements, et la variante dangereuse - un succès renvoyé avant que l'événement ne soit durable
  • Buffer du producteur : le client bloque pendant un temps borné, puis lève une erreur, et le code qui attrape cette erreur sans rien compter, c'est là que la donnée meurt
  • Durabilité du broker : sans acquittement requis, rien ne garantit que l'enregistrement est arrivé ; avec le seul leader, il est perdu si ce leader tombe avant que les followers ne répliquent
  • L'acquittement par toutes les répliques n'est pas de la durabilité : il attend l'ensemble in-sync courant, dont la taille minimale vaut un par défaut, donc un pic qui fait décrocher les followers valide sur le seul leader
  • Consommateur : commiter l'offset avant le traitement, c'est de l'at-most-once, et c'est un réglage par défaut plutôt qu'une décision - le client commite sur un timer à moins que cela n'ait été désactivé
  • Dépassement de rétention : un consommateur qui prend du retard au-delà de la fenêtre de rétention trouve son offset suivant supprimé, et la politique de réinitialisation par défaut le fait sauter à la tête du log
  • Le chargement dans la base colonnaire : les insertions fire-and-forget acquittent dès la mise en buffer, et les vues matérialisées dépendantes dédupliquent via un réglage distinct - c'est là que la table brute et le rapport divergent

La règle est une règle de reporting, pas d'ingénierie : une perte est attribuée à un raccord nommé, ou n'est pas attribuée du tout. Deux alarmes gardent visibles les raccords silencieux - le lag du consommateur mesuré en temps face à la rétention, et une politique de réinitialisation qui échoue au lieu de sauter.

La déduplication a besoin d'une clé qui existe avant le premier retry

Une clé de déduplication n'est pas une clé primaire commode choisie à la destination. Elle est attribuée en amont de tout retry, au moment de l'enchère ou à la création de l'événement, et le récepteur n'en invente jamais. Les timestamps d'arrivée sont exclus : un retry porte une nouvelle heure d'arrivée et une nouvelle clé.

La clé doit être identique à chaque tentative, ce qui décide de ses composants : l'ad exchange ou le seat, l'identifiant d'enchère, l'identifiant d'impression et le type d'événement. Partitionnez le topic sur cette clé, pour que les retries atterrissent ensemble et que l'ordre par clé leur survive. Cette consigne ne vaut que pour le transport : le même mot appliqué à la base colonnaire donne une partition par événement, et l'insertion meurt sur la limite par bloc. Partitionnez le stockage par le temps, ordonnez par la clé.

  • Le producteur idempotent supprime les doublons issus des retries du producteur à l'intérieur d'une même session, et Kafka l'active par défaut depuis 3.0, aux côtés de l'acquittement par toutes les répliques
  • Ce réglage par défaut est conditionnel : un paramètre en conflit venant d'une configuration plus ancienne désactive l'idempotence en silence, alors demandez au processus en cours ce qu'il a
  • Elle ne voit pas un doublon au niveau applicatif : un processus qui a planté puis renvoyé, un beacon déclenché deux fois, un opérateur qui relance un job d'ingestion
  • La déduplication d'insertion de ClickHouse hache le contenu du bloc : un consommateur qui refait ses batches après un rebalance envoie donc les mêmes lignes dans une nouvelle forme, et le hash ne correspond plus
  • La fenêtre est bornée en blocs et dans le temps, et sur les tables non répliquées elle vaut zéro par défaut, c'est-à-dire désactivée ; un token d'insertion supprime cette dépendance
  • L'exactly-once à l'intérieur du log couvre le consume-transform-produce, et le saut vers une base analytique se situe hors de cette frontière, quoi que promette le transport

La forme qui fonctionne est donc un transport at-least-once avec des clés idempotentes. La déduplication à l'écriture garde la facture de stockage raisonnable ; c'est la déduplication à la lecture qui rend le chiffre correct. Les merges ne combinent jamais des parts de partitions différentes : un doublon qui atterrit dans la partition suivante n'est donc résolu que lorsqu'une requête le demande.

La back pressure décide si une perte est un chiffre ou une rumeur

En surcharge, un système a trois options : ralentir le producteur, délester avec un compteur, ou perdre en silence. Seule la troisième est inacceptable, et c'est le comportement par défaut d'un code à qui on n'a jamais posé la question. La perte au pic, c'est une file qui a grandi jusqu'à épuiser la mémoire, ou un acquittement émis avant la durabilité.

  • Des files bornées à chaque saut, avec un rejet explicite au lieu de la croissance. Une file non bornée déplace la perte vers la pression mémoire et un redémarrage
  • Le temps de blocage du producteur et la taille du buffer sont des décisions de capacité : dimensionnez-les à partir du pic que vous avez mesuré, et alertez sur le temps passé bloqué
  • Délestez par classe plutôt qu'au hasard : les événements d'impression et les événements facturables survivent, les diagnostics partent en premier, et chaque événement délesté incrémente un compteur étiqueté
  • Le lag du consommateur, c'est la back pressure rendue visible. Alertez sur l'âge du plus ancien événement non traité et sur la vitesse à laquelle le lag change

Un rejet doté d'un compteur étiqueté est une quantité connue, réconciliable plus tard. Un rejet sans compteur n'est pas une donnée perdue, c'est un chiffre perdu.

L'arrivée tardive est structurelle, et la moitié de l'écart est une affaire de calendrier

Les recommandations d'implémentation OpenRTB le disent directement : la séquence qui va de la demande publicitaire à l'affichage et à la facturation, en passant par l'enchère, n'est fondamentalement pas transactionnelle. Trop de parties se tiennent entre les deux décomptes.

Le délai est attendu plutôt qu'exceptionnel. La bid request peut porter une expiration d'impression, et le bid le délai que le bidder tolère ; les mêmes recommandations donnent des ordres de grandeur allant de l'ordre de la minute pour le web à bien plus pour les formats in-app mis en cache et la vidéo stitchée.

Aucun des deux champs n'est un contrat. Les recommandations le disent clairement : une notification de facturation qui arrive après l'expiration déclarée par un bidder peut rester facturable - une discussion de politique entre le bidder et l'ad exchange plutôt qu'une contrainte imposée par le protocole.

  • Il existe trois timestamps par événement et un seul pilote la fenêtre : l'horloge de l'appareil, non fiable ; l'heure de réception à l'edge, tardive ; l'heure de l'enchère, qui fait autorité
  • Un quatrième existe là où l'ad exchange le fournit, la macro qui porte le moment où l'impression a été délivrée ; là où il est absent, la spécification suppose que la notification a été suivie à quelques secondes d'intervalle
  • Un watermark déclare que le temps d'événement a atteint un point donné et qu'aucun élément antérieur n'est attendu : le retard autorisé est donc un paramètre que vous choisissez
  • Publiez la fenêtre de retard par type d'événement avec la politique de retraitement : les chiffres bougent tant qu'elle est ouverte, puis se figent, et les mouvements sont journalisés
  • Comptez l'événement en retard et marquez-le comme tardif, car le rejeter revient à rejeter de la dépense qui vous a été facturée

Un événement qui arrive après la fenêtre n'est pas un défaut du pipeline, c'est une propriété du média. Le seul vrai choix est de savoir si le chiffre bouge en public pendant que la fenêtre est ouverte, ou s'il bouge en privé après.

La réconciliation joint sur les identifiants que le protocole transporte déjà

Les identifiants que l'ad exchange substitue dans les URL de notification et de tracking sont l'identifiant d'enchère issu de la bid request, l'identifiant d'impression et, là où le bidder en a généré un, l'identifiant de bid. Aucun des trois n'est une clé à lui seul.

La spécification qualifie l'identifiant d'enchère d'unique au niveau de l'ad exchange, pas d'unique au niveau mondial : deux ad exchanges peuvent vous remettre la même chaîne le même jour. L'identifiant d'impression n'est unique qu'à l'intérieur de sa propre bid request, souvent le littéral 1. L'identifiant de bid est optionnel.

La clé qui tient est la clé composite : l'ad exchange ou le seat par lequel vous avez transigé, plus l'identifiant d'enchère, plus l'identifiant d'impression. Générez-la côté bidder au moment de l'enchère, et traitez tout ce qui est plus court comme un préfixe et non comme une clé.

Si ces macros sont absentes des beacons, la réconciliation au niveau de l'événement est impossible, et il ne reste qu'un rapprochement sur le temps, l'emplacement et la créative. Ce que vous pouvez construire à la place, c'est un pont de six décomptes, chacun nommant sa raison de différer de l'étape précédente.

  • Les enchères gagnées, d'après le log du bidder - le seul décompte qui vous appartient entièrement
  • Les win notices reçues par l'ad exchange - la différence, ce sont les pertes de notification et les timeouts, et selon la spécification une win notice n'implique pas nécessairement la diffusion
  • Les beacons reçus à votre edge - la différence, c'est la collecte côté client et tous les raccords au-dessus
  • Les événements après déduplication - la différence, ce sont les retries, et elle doit rester stable d'une semaine à l'autre
  • Les événements après filtrage du trafic invalide - la différence, c'est un taux de filtrage que vous publiez au lieu de le découvrir
  • Les événements facturables - la différence, c'est la règle de facturation, et la notification appartient au côté serveur, là où l'ad exchange comptabilise le revenu

Un ratio stable entre étapes est l'objectif, et un mouvement inexpliqué est l'alarme. À l'intérieur d'un même système, les ratios de saut doivent valoir un, et tout écart est le signal. À la frontière entre l'enchère et la mesure, c'est un qui devient la lecture suspecte.

Les mêmes compteurs, lus comme des ratios, transforment la question en arithmétique : acceptés sur envoyés, produits sur acceptés, consommés sur produits, insérés sur consommés. Quatre ratios sur un même graphique disent où sont partis les événements avant que quiconque n'ouvre un log. Ajoutez un numéro de séquence côté producteur par source et par partition, et un trou devient une preuve plutôt qu'un soupçon.

Ce qu'un pipeline reconstruit ne vous donne pas

Une question à laquelle le pont ne répond pas et à laquelle un contrat répond : sur lequel de ces chiffres vous payez. Le vendeur comptabilise son revenu sur son propre événement facturable, l'acheteur fait son pacing sur le sien, et les recommandations traitent un écart persistant comme une conversation de support entre les parties.

Décidez à l'avance quel décompte fait foi pour la dépense, et à partir de quel écart une note de rapport devient un ticket auprès de l'ad exchange. Le travail ci-dessus achète l'attribution des pertes, des doublons honnêtes et une réconciliation expliquée ligne par ligne. Il n'achète pas ce qui suit.

  • Cela ne rend pas les deux décomptes égaux : les deux côtés comptent délibérément des événements différents, et la différence s'explique, elle ne se supprime jamais
  • Cela ne récupère pas les événements rejetés avant que l'instrumentation n'existe - la courbe de stabilisation commence le jour où les compteurs commencent
  • Cela ne supprime pas le retraitement : la journée d'hier bouge tant que la fenêtre de retard est ouverte, et une activité qui ne peut pas le tolérer a besoin d'une clôture plus tardive
  • Cela ne survit pas à des macros manquantes : sans les identifiants d'enchère dans les beacons, aucune conception de stockage ne produit une jointure au niveau de l'événement
  • Cela ne rend pas les données échantillonnées jointables après coup, car l'échantillonnage décide, avant que la ligne ne soit écrite, à quelles questions il restera possible de répondre
  • Cela ne remplace pas la liste de divulgations qu'attend un audit de type MRC : point de capture, fréquence de journalisation, estimations de latence, règles en cas d'incohérences

Un pipeline capable de répondre à ces questions de divulgation a une histoire d'intégrité. Celui qui ne le peut pas a un avis, et un avis, c'est ce sur quoi on se dispute en fin de trimestre.

La panne contre laquelle il vaut la peine de concevoir n'est pas l'heure manquante qui déclenche une enquête. C'est la version silencieuse : un raccord qui déleste sans compteur, une insertion acquittée avant d'être durable, et une réconciliation sur une fenêtre encore ouverte.

Ce qu'amBrain peut étayer publiquement : amBrain est une société d'ingénierie logicielle basée à Erevan, en Arménie, qui construit des plateformes de trading à faible latence, des matching engines et des systèmes de real-time bidding en Rust. amBrain construit des logiciels depuis 2019. Reconstruire un pipeline de mesure n'est pas un travail décrit ici. Si les chiffres cessent de concorder côté bidder et côté ad exchange - les enregistrements de bid, de win et d'impression eux-mêmes - c'est la conversation qui vaut la peine d'avoir lieu, et elle commence par la courbe de stabilisation plutôt que par une reconstruction.

Vous avez une architecture de ce type sur la table ?

Apportez votre architecture actuelle et le mode de défaillance qui vous inquiète : nous les passerons en revue ensemble en une demi-heure.

Articles liés

Erreur de chargement de l'image
AdTech
Sep 10, 2026Lecture 10 min

Pauses GC Go dans un bidder RTB : mark assist, échéances et la décision Rust

Lire l'article
Erreur de chargement de l'image
AdTech
Mar 5, 20267 min de lecture

Comment l'IA redessine la publicité programmatique en 2026

Lire l'article
Erreur de chargement de l'image
AdTech
Feb 14, 20266 min de lecture

Ciblage privacy-first : construire de l'ad tech sans cookies tiers

Lire l'article