AdTechSep 10, 202610 min de lectura

La medición publicitaria pierde eventos en el pico: costuras, claves duplicadas y el join con el bidder

Pipelines de eventosMedición publicitariaAtribuciónIntegridad de datos
Error al cargar la imagen

Tu pipeline de medición pierde eventos en el pico de tráfico y los números de atribución nunca cuadran con los logs del bidder. Son dos fallos detrás de un mismo síntoma: eventos que nunca llegaron, en una costura que nadie contó, y eventos que llegaron y se contaron bajo otra regla. Así se construyen las costuras, las claves, la ventana de llegadas tardías y el puente de conciliación.

Un pipeline de eventos que pierde eventos en el pico de tráfico y nunca cuadra con los logs del bidder puede ser dos fallos detrás de un mismo síntoma. Uno es de transporte: eventos creados que nunca aterrizaron, en una costura que nadie contó. El otro es de definición: eventos que sí aterrizaron y se contaron bajo una regla distinta a la del registro de subasta.

El planteamiento habitual - el pipeline descarta eventos, así que reconstruye el pipeline - arregla como mucho uno de ellos. Lo que sigue separa los dos y dice qué produce una conciliación, que no es igualdad. Los valores por defecto que se citan abajo son los de Kafka en el lado del transporte y los de ClickHouse en el lado del almacenamiento.

La respuesta corta es estructural: un identificador por subasta transportado de extremo a extremo, un contador a ambos lados de cada salto y una conciliación sobre una ventana que ya se ha cerrado. Lo que amBrain puede sustentar públicamente es el trabajo de AdTech: desarrollo de DSP, plataformas de real-time bidding e ingeniería de ad exchange. En cualquier stack de RTB ese es el lado del que vienen los registros de puja, win e impresión. El pipeline de abajo se describe desde la mecánica del problema, no desde un caso nuestro.

Una consulta distingue un evento perdido de uno tardío

El primer fallo es la pérdida: el evento se creó y nunca llegó, en un salto concreto, por un motivo concreto. Un beacon que nunca salió de la página, un edge reiniciado a mitad de despliegue, un buffer de producer que se llenó, un consumer que confirmó su offset antes de procesar.

El segundo no es un fallo en absoluto. El lado de la medición cuenta un evento iniciado en el cliente; el bidder registra un resultado de subasta del lado del servidor. Uno es una adjudicación, el otro una observación de lo que le pasó. Las guías de medición del MRC tratan pre-fetch, pre-render y auto-refresh como cosas distintas que hay que detectar y divulgar: una regla de conteo, no un fallo de transporte. Distinguir los dos cuesta una consulta y algo de paciencia.

  • Vuelve a ejecutar la misma ventana de event time una hora, seis horas y un día completo después del periodo que cubre
  • Un déficit que se reduce en cada ejecución significa que los eventos llegaron tarde, no que se perdieron, y que el transporte está bien
  • Cuenta claves de deduplicación distintas, no filas: el transporte at-least-once garantiza reentregas, y una curva basada en filas oculta un sobreconteo
  • Un déficit que se mantiene plano significa que los eventos ya no están, y la pregunta es en qué costura
  • La prueba necesita un timestamp de event time, un identificador que sobreviva a los reintentos y retención suficiente para volver a ejecutar la ventana
  • Publica la curva de estabilización como gráfico, por tipo de evento, junto al número que explica

Hasta que esa curva exista, los dos lados del argumento son opiniones. Después, su forma decide qué mitad de este artículo aplica, y las mitades no son excluyentes.

Los eventos se pierden en costuras con nombre, y a una costura sin contar no se la puede culpar

Hay siete sitios donde un evento publicitario se crea y luego deja de existir en silencio, más un ajuste que parece una garantía y no lo es.

  • Recogida en el cliente: el beacon se dispara pero el documento hace unload antes, o la creatividad estaba cacheada, pre-fetched o auto-refreshed y cuenta lo que el registro de subasta no cuenta
  • Ingesta en el edge: límites de conexión, agotamiento de keep-alive, reinicios durante los despliegues y la variante peligrosa - éxito devuelto antes de que el evento sea durable
  • Buffer del producer: el cliente bloquea durante un tiempo acotado, luego lanza el error, y el código que captura ese error y no cuenta nada es donde muere el dato
  • Durabilidad del broker: sin acuse requerido nada garantiza que el registro llegó; solo con el leader se pierde si ese leader cae antes de que los followers repliquen
  • El acuse de todas las réplicas no es durabilidad: espera al conjunto in-sync actual, cuyo tamaño mínimo es uno por defecto, así que un pico que deja rezagados a los followers confirma solo en el leader
  • Consumer: confirmar el offset antes de procesar es at-most-once, y es un valor por defecto más que una decisión - el cliente hace commit por temporizador salvo que eso se haya desactivado
  • Desbordamiento de la retención: un consumer que se retrasa más allá de la ventana de retención encuentra borrado su siguiente offset, y la política de reset por defecto lo salta a la cabeza del log
  • Carga en el almacén columnar: los inserts fire-and-forget confirman en cuanto quedan en buffer, y las vistas materializadas dependientes deduplican mediante un ajuste aparte - ahí es donde la tabla cruda y el informe se separan

La regla es una regla de reporting, no de ingeniería: una pérdida se atribuye a una costura con nombre, o no se atribuye en absoluto. Dos alarmas mantienen visibles las costuras silenciosas - el consumer lag medido en tiempo frente a la retención, y una política de reset que falla en vez de saltar.

La deduplicación necesita una clave que exista antes del primer reintento

Una clave de deduplicación no es una clave primaria cómoda elegida en el destino. Se asigna aguas arriba de cualquier reintento, en el momento de la subasta o cuando se crea el evento, y el receptor nunca se la inventa. Los timestamps de llegada quedan fuera: un reintento lleva un timestamp de llegada nuevo y una clave nueva.

La clave tiene que ser idéntica en todos los intentos, lo que decide sus partes: el exchange o seat, el identificador de subasta, el identificador de impresión y el tipo de evento. Particiona el topic por esa clave, para que los reintentos caigan juntos y el orden por clave sobreviva. Esa instrucción es solo para el transporte: la misma palabra aplicada al almacén columnar da una partición por evento, y el insert muere en el límite por bloque. Particiona el almacenamiento por tiempo y ordena por la clave.

  • El producer idempotente elimina los duplicados de los reintentos del producer dentro de una misma sesión, y Kafka lo activa por defecto desde 3.0 junto con el acuse de todas las réplicas
  • Ese valor por defecto es condicional: un ajuste en conflicto procedente de una configuración antigua desactiva la idempotencia en silencio, así que pregúntale al proceso en marcha qué tiene
  • No puede ver un duplicado a nivel de aplicación: un proceso que cayó y reenvió, un beacon disparado dos veces, un operador que vuelve a ejecutar un trabajo de ingesta
  • La deduplicación de inserts de ClickHouse hashea el contenido del bloque, así que un consumer que rehace los lotes tras un rebalanceo envía las mismas filas con otra forma y el hash falla
  • La ventana está acotada en bloques y en tiempo, y en tablas no replicadas vale cero por defecto, es decir, desactivada; un token de insert elimina esa dependencia
  • El exactly-once dentro del log cubre consume-transform-produce, y el salto hacia una base de datos analítica queda fuera de esa frontera, prometa lo que prometa el transporte

Así que la forma que funciona es transporte at-least-once con claves idempotentes. La deduplicación en escritura mantiene razonable la factura de almacenamiento; la deduplicación en lectura es la que hace correcto el número. Los merges nunca combinan parts de particiones distintas, así que un duplicado que cae en la partición siguiente solo se resuelve cuando una consulta lo pide.

El back pressure decide si una pérdida es un número o un rumor

Bajo sobrecarga un sistema tiene tres opciones: frenar al producer, descartar con un contador, o perder en silencio. Solo la tercera es inaceptable, y es el comportamiento por defecto del código al que nunca se le hizo la pregunta. La pérdida en el pico es una cola que creció hasta agotar la memoria, o un acuse emitido antes de la durabilidad.

  • Colas acotadas en cada salto, con rechazo explícito en vez de crecimiento. Una cola no acotada traslada la pérdida a la presión de memoria y a un reinicio
  • El tiempo de bloqueo del producer y el tamaño del buffer son decisiones de capacidad: dimensiónalos a partir del pico que mediste, y alarma sobre el tiempo pasado en bloqueo
  • Descarta por clase y no al azar: los eventos de impresión y los facturables sobreviven, los de diagnóstico caen primero, y cada evento descartado incrementa un contador etiquetado
  • El consumer lag es back pressure hecho visible. Alarma sobre la antigüedad del evento sin procesar más viejo y sobre lo rápido que cambia el lag

Un descarte con un contador etiquetado es una cantidad conocida que se puede conciliar después. Un descarte sin él no son datos perdidos, es un número perdido.

La llegada tardía es estructural, y la mitad del descuadre es un calendario

La guía de implementación de OpenRTB lo dice directamente: la secuencia que va de la solicitud de anuncio a la subasta y de ahí al renderizado y la facturación es fundamentalmente no transaccional. Hay demasiadas partes entre los dos recuentos.

El retraso es lo esperado, no la excepción. La solicitud de puja puede llevar una caducidad de la impresión y la puja, el retraso que el bidder tolera, y la misma guía da reglas generales que van del orden de un minuto para web a mucho más para formatos in-app cacheados y vídeo con stitching.

Ninguno de los dos campos es un contrato. La guía dice claramente que una notificación de facturación que llega más tarde que la caducidad declarada por el bidder puede seguir siendo facturable - una discusión de política entre bidder y exchange antes que algo que imponga el protocolo.

  • Existen tres timestamps por evento y exactamente uno gobierna la ventana: el reloj del dispositivo, no fiable; la hora de recepción en el edge, tardía; la hora de la subasta, autoritativa
  • Existe un cuarto cuando el exchange lo proporciona: la macro que lleva el momento en que se cumplió la impresión; donde falta, la especificación asume el momento de la notificación más unos segundos
  • Un watermark declara que el event time ha alcanzado un punto y que no se esperan elementos anteriores, así que el retraso permitido es un parámetro que eliges
  • Publica la ventana de llegadas tardías por tipo de evento junto con la política de reexpresión: los números se mueven mientras está abierta, luego se congelan, y los movimientos quedan registrados
  • Cuenta el evento tardío y márcalo como tardío, porque descartarlo es descartar gasto que te facturaron

Un evento que llega después de la ventana no es un defecto del pipeline, es una propiedad del medio. La única elección real es si el número se mueve en público mientras la ventana está abierta, o se mueve en privado después.

La conciliación hace join por los identificadores que el protocolo ya transporta

Los identificadores que el exchange sustituye en las URLs de notificación y de tracking son el identificador de subasta de la solicitud de puja, el identificador de impresión y, cuando el bidder acuñó uno, el identificador de puja. Ninguno de los tres es una clave por sí solo.

La especificación llama al identificador de subasta único por exchange, no único a nivel global: dos exchanges pueden entregarte la misma cadena el mismo día. El identificador de impresión solo es único dentro de su propia solicitud de puja, a menudo el literal 1. El identificador de puja es opcional.

La clave que aguanta es la compuesta: el exchange o seat a través del cual transaccionaste, más el identificador de subasta, más el identificador de impresión. Acúñala del lado del bidder en el momento de la subasta, y trata cualquier cosa más corta como un prefijo y no como una clave.

Si esas macros faltan en los beacons, la conciliación a nivel de evento es imposible, y lo que queda es casar por tiempo, emplazamiento y creatividad. Lo que sí puedes construir es un puente de seis recuentos, cada uno nombrando su motivo de diferencia con el paso anterior.

  • Subastas ganadas, del log del bidder - el único recuento que es enteramente tuyo
  • Notificaciones de win recibidas por el exchange - la diferencia es la pérdida de notificaciones y los timeouts, y según la especificación una notificación de win no implica necesariamente la entrega
  • Beacons recibidos en tu edge - la diferencia es la recogida en el cliente y todas las costuras por encima de ella
  • Eventos tras la deduplicación - la diferencia son los reintentos, y debería ser estable semana a semana
  • Eventos tras el filtrado de tráfico inválido - la diferencia es una tasa de filtrado que publicas en vez de descubrir
  • Eventos facturables - la diferencia es la regla de facturación, y la notificación va del lado del servidor, donde el exchange contabiliza los ingresos

Un ratio estable entre pasos es el objetivo y un movimiento sin explicación es la alarma. Dentro de un mismo sistema, los ratios por salto deben estar en uno, y cualquier desviación es la señal. Cruzando la frontera de la subasta a la medición, uno es la lectura sospechosa.

Los mismos contadores, leídos como ratios, convierten la pregunta en aritmética: aceptados sobre enviados, producidos sobre aceptados, consumidos sobre producidos, insertados sobre consumidos. Cuatro ratios en un gráfico dicen adónde fueron los eventos antes de que nadie abra un log. Añade un número de secuencia del lado del producer por fuente y partición, y un hueco pasa a ser evidencia en vez de sospecha.

Lo que un pipeline reconstruido no te da

Una pregunta que el puente no responde y un contrato sí: sobre cuál de estos números pagas. El vendedor contabiliza los ingresos sobre su propio evento facturable, el comprador hace su pacing sobre el suyo, y la guía trata una diferencia persistente como una conversación de soporte entre las partes.

Decide de antemano qué recuento es el sistema de registro del gasto, y a partir de qué diferencia una nota en el informe se convierte en un ticket con el exchange. El trabajo de arriba compra atribución de la pérdida, duplicados honestos y una conciliación explicada línea a línea. No compra lo siguiente.

  • No hace iguales los dos recuentos: los dos lados cuentan eventos distintos a propósito, y la diferencia se explica, nunca se elimina
  • No recupera los eventos descartados antes de que existiera la instrumentación - la curva de estabilización empieza el día en que empiezan los contadores
  • No elimina la reexpresión: el día de ayer se mueve mientras la ventana de llegadas tardías está abierta, y un negocio que no puede tolerarlo necesita un cierre más tardío
  • No sobrevive a macros ausentes: sin los identificadores de subasta en los beacons, ningún diseño de almacenamiento produce un join a nivel de evento
  • No hace que los datos muestreados admitan un join después, porque el muestreo decide qué preguntas siguen teniendo respuesta antes de que se escriba la fila
  • No sustituye la lista de divulgaciones que espera una auditoría al estilo MRC: punto de captura, frecuencia de logging, estimaciones de latencia, reglas para las inconsistencias

Un pipeline que puede responder a esas preguntas de divulgación tiene un argumento de integridad. El que no puede tiene una opinión, y una opinión es lo que se discute al cierre de un trimestre.

El fallo contra el que merece la pena diseñar no es la hora que falta y arranca una investigación. Es la versión silenciosa: una costura que descarta sin contador, un insert confirmado antes de ser durable, y una conciliación sobre una ventana todavía abierta.

Lo que amBrain puede sustentar públicamente: amBrain es una empresa de ingeniería de software de Ereván, Armenia, que construye plataformas de trading de baja latencia, matching engines y sistemas de real-time bidding en Rust. amBrain lleva construyendo software desde 2019. Reconstruir un pipeline de medición no es el trabajo que se describe aquí. Si los números dejan de cuadrar en el lado del bidder y del exchange - los propios registros de puja, win e impresión -, esa es la conversación que merece la pena, y empieza por la curva de estabilización y no por una reconstrucción.

¿Tiene un diseño así sobre la mesa?

Traiga su arquitectura actual y el modo de fallo que le preocupa, y lo repasaremos juntos en media hora.

Artículos relacionados

Error al cargar la imagen
AdTech
Sep 10, 202610 min de lectura

Pausas del GC de Go en un bidder RTB: mark assist, deadlines y la decisión de Rust

Leer artículo
Error al cargar la imagen
AdTech
Mar 5, 20267 min de lectura

Cómo la IA está transformando la publicidad programática en 2026

Leer artículo
Error al cargar la imagen
AdTech
Feb 14, 20266 min de lectura

Segmentación privacy-first: construir ad tech sin cookies de terceros

Leer artículo