Ваш измерительный пайплайн теряет события на пике трафика, а цифры атрибуции никогда не сходятся с логами биддера. Это два отказа за одним симптомом: события, которые не дошли, на стыке, который никто не считал, и события, которые дошли и были посчитаны по другому правилу. Здесь — как строятся стыки, ключи, окно опозданий и мост сверки.
Пайплайн событий, который теряет события на пике трафика и никогда не сходится с логами биддера, — это могут быть два отказа за одним симптомом. Первый транспортный: события созданы и не доехали, на стыке, который никто не считал. Второй — в определениях: события доехали и были посчитаны по другому правилу, чем запись аукциона.
Обычная постановка — «пайплайн теряет события, значит, надо переписать пайплайн» — чинит в лучшем случае одну из них. Дальше эти две разделены и сказано, что даёт сверка, а даёт она не равенство. Дефолты, которые цитируются ниже, — Kafka на транспорте и ClickHouse на хранении.
Короткий ответ структурный: один идентификатор на аукцион, проносимый насквозь, счётчик по обе стороны каждого перехода и сверка по уже закрытому окну. Что amBrain может подтвердить публично — это работа в AdTech: разработка DSP, платформ real-time bidding и ad exchange. В любом RTB-стеке именно с этой стороны приходят записи о ставке, выигрыше и показе. Пайплайн ниже описан из механики задачи, а не из нашего кейса.
Первый отказ — потеря: событие создано и не дошло, на конкретном переходе, по конкретной причине. Бикон, который так и не ушёл со страницы, эдж, перезапущенный посреди деплоя, заполнившийся буфер продюсера, консьюмер, закоммитивший оффсет до обработки.
Второй — вообще не отказ. Сторона измерения считает событие, инициированное клиентом; биддер записывает результат аукциона на серверной стороне. Одно — присуждение, другое — наблюдение за тем, что с ним дальше случилось. Руководства MRC по измерению трактуют pre-fetch, pre-render и auto-refresh как отдельные вещи, которые нужно детектировать и раскрывать: это правило подсчёта, а не сбой транспорта. Отличить одно от другого стоит одного запроса и некоторого терпения.
Пока этой кривой нет, обе стороны спора — мнения. Когда она появится, её форма решает, какая половина этой статьи применима, и половины не исключают друг друга.
Есть семь мест, где рекламное событие создаётся, а потом тихо перестаёт существовать, плюс одна настройка, которая выглядит гарантией и ею не является.
Правило здесь отчётное, а не инженерное: потеря относится к названному стыку либо не относится никуда. Тихие стыки держат на виду два аларма — отставание консьюмера, измеренное во времени относительно retention, и политика сброса, которая падает с ошибкой, а не прыгает.
Ключ дедупликации — не удобный первичный ключ, выбранный на приёмнике. Он назначается выше по потоку от любой повторной отправки — в момент аукциона или в момент создания события, — и получатель никогда его не выдумывает. Отметки времени прибытия в него не входят: повтор несёт новое время прибытия и новый ключ.
Ключ обязан быть одинаковым во всех попытках, и это определяет его части: биржа или seat, идентификатор аукциона, идентификатор показа и тип события. Партиционируйте топик по этому ключу, чтобы повторы попадали вместе и порядок внутри ключа их переживал. Эта инструкция только про транспорт: то же слово, применённое к колоночному хранилищу, даёт по партиции на событие, и вставка умирает на лимите партиций в блоке. Хранилище партиционируйте по времени, а сортируйте по ключу.
Поэтому рабочая схема — транспорт at-least-once с идемпотентными ключами. Дедупликация на записи держит счёт за хранение в разумных пределах; верной цифру делает дедупликация на чтении. Слияния никогда не объединяют куски из разных партиций, поэтому дубликат, попавший в следующую партицию, разрешается только тогда, когда его спросит запрос.
При перегрузке у системы три варианта: притормозить продюсера, сбросить нагрузку со счётчиком или потерять тихо. Неприемлем только третий, и он же — поведение по умолчанию у кода, которому этот вопрос никогда не задавали. Потеря на пике — это очередь, которая росла, пока не кончилась память, или подтверждение, выданное до сохранности.
Отброс с размеченным счётчиком — известная величина, её можно свести позже. Отброс без счётчика — это не потерянные данные, это потерянная цифра.
Руководство по внедрению OpenRTB говорит прямо: последовательность от рекламного запроса через аукцион к рендерингу и биллингу принципиально не транзакционна. Между двумя цифрами стоит слишком много сторон.
Задержка ожидаема, а не исключительна. Bid request может нести срок истечения показа, а ставка — задержку, которую биддер терпит, и то же руководство даёт прикидки от порядка минуты для веба до куда больших значений для кешированных in-app форматов и сшитого видео.
Ни одно из этих полей не контракт. Руководство прямо говорит, что уведомление о биллинге, пришедшее позже объявленного биддером срока истечения, всё равно может быть оплачиваемым, — это разговор о политике между биддером и биржей, а не то, что навязывает протокол.
Событие, пришедшее после окна, — не дефект пайплайна, а свойство среды. Настоящий выбор здесь один: движется ли цифра публично, пока окно открыто, или меняется непублично уже после.
Идентификаторы, которые биржа подставляет в URL уведомлений и трекинга, — это идентификатор аукциона из bid request, идентификатор показа и, если биддер его создал, идентификатор ставки. Ни один из трёх сам по себе не ключ.
Спецификация называет идентификатор аукциона уникальным в пределах биржи, а не глобально: две биржи могут выдать вам одну и ту же строку в один день. Идентификатор показа уникален только внутри своего bid request и часто равен буквально 1. Идентификатор ставки необязателен.
Держится составной ключ: биржа или seat, через которые прошла сделка, плюс идентификатор аукциона, плюс идентификатор показа. Создавайте его на стороне биддера в момент аукциона, а всё, что короче, считайте префиксом, а не ключом.
Если этих макросов в биконах нет, сверка на уровне событий невозможна, и остаётся сопоставление по времени, плейсменту и креативу. Построить вместо неё можно мост из шести цифр, каждая из которых называет свою причину отличия от предыдущего шага.
Цель — стабильное отношение между шагами, тревога — необъяснённое движение. Внутри одной системы отношения на переходах должны стоять на единице, и любое отклонение — сигнал. На границе между аукционом и измерением подозрительное показание — как раз единица.
Те же счётчики, прочитанные как отношения, превращают вопрос в арифметику: принято к отправленному, отправлено в брокер к принятому, вычитано к отправленному в брокер, вставлено к вычитанному. Четыре отношения на одном графике говорят, куда делись события, ещё до того, как кто-то откроет лог. Добавьте на стороне продюсера порядковый номер по каждому источнику и партиции — и дырка станет доказательством, а не подозрением.
На один вопрос мост не отвечает, а контракт отвечает: по какой из этих цифр вы платите. Продавец проводит выручку по своему оплачиваемому событию, покупатель распределяет бюджет по своему, а руководство считает устойчивое расхождение поводом для разговора между сторонами через поддержку.
Решите заранее, какой счёт является системой учёта для расхода и при каком расхождении примечание в отчёте становится тикетом к бирже. Работа выше покупает атрибуцию потерь, честные дубликаты и сверку, объяснённую построчно. Она не покупает следующего.
У пайплайна, который может ответить на эти вопросы о раскрытии, есть история целостности. У того, который не может, есть мнение, а мнение — это то, о чём спорят в конце квартала.
Отказ, от которого стоит защищаться проектированием, — не пропавший час, с которого начинается расследование. Это тихая версия: стык, который сбрасывает без счётчика, вставка, подтверждённая до сохранности, и сверка по окну, которое ещё открыто.
Что amBrain может подтвердить публично: amBrain — инженерная компания из Еревана, Армения: строим low latency торговые платформы, matching engine и системы real-time bidding на Rust. amBrain пишет софт с 2019 года. Переписывание измерительного пайплайна — не та работа, которая здесь описана. Если цифры перестают сходиться на стороне биддера и биржи — на самих записях о ставке, выигрыше и показе, — вот об этом стоит разговаривать, и начинается он с кривой доуточнения, а не с переписывания.
Принесите текущую архитектуру и тот сценарий отказа, который вас беспокоит, — разберём его вместе за полчаса.