Как зафиксировать задачу CTR dashboard
С чего начать system design для realtime dashboard CTR рекламных кампаний?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Сначала зафиксировать пользователя, основной экран, freshness, гранулярность графика и SLA, затем перейти к событиям impressions/clicks.
Подробный разбор
В начале такого дизайна нужно не рисовать Kafka, а уточнить продуктовый контракт: кто смотрит dashboard, по каким campaign_id, какие окна нужны, насколько realtime должен быть результат и что значит "CTR сейчас".
После этого фиксируются базовые события: impression и click. Для каждого события нужны timestamp, campaign/ad/account id, event type и технический id для дедупликации. Только после requirements можно считать throughput и выбирать ingestion, aggregation и storage.
Какие события и масштабы уточнять для CTR
Какие события, сущности и масштабы нужно уточнить перед проектированием ads CTR dashboard?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Минимум нужны impressions, clicks, campaign/account dimensions, суточный поток, cardinality кампаний и требования по задержке обновления.
Подробный разбор
CTR считается как clicks / impressions, поэтому сырой поток должен надежно отличать показы и клики. Для sizing нужно уточнить events per day/second, количество рекламодателей, кампаний, активных кампаний одновременно и распределение нагрузки.
Также важны требования к задержке: dashboard за секунды, минуты или часы. От этого зависит, достаточно ли batch aggregation, нужен ли streaming job, какие окна материализовать заранее и какой storage выдержит частые range queries по campaign_id и времени.
Как посчитать поток событий для CTR dashboard
Нужно построить систему, где рекламодатель смотрит CTR кампаний. Дано 200 млрд показов в день и CTR около 1%. Как начать system design с чисел?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
200 млрд показов в день - это примерно 2.3 млн impression events/sec в среднем, плюс около 23 тыс click events/sec при CTR 1%. Дальше закладываем peak multiplier.
Подробный разбор
Старт system design - уточнить требования и посчитать порядок нагрузки. 200 млрд показов в день делим на 86400 секунд: получается около 2.3 млн impressions/sec в среднем. Если CTR около 1%, кликов около 2 млрд в день, то есть примерно 23 тыс clicks/sec.
Средняя нагрузка не равна peak. Для рекламы обычно есть суточная сезонность, кампании, регионы, всплески. Поэтому добавляем peak multiplier, например 3-5x, и отдельно обсуждаем задержку обновления dashboard: 30-60 секунд, минуты или near real-time.
После чисел фиксируем сущности: advertiser, campaign, ad, impression, click, timestamp, region/device, cost. Основной output - временные ряды CTR по campaign_id и bucket времени.
Типичные ошибки
- Не перевести events/day в events/sec.
- Забыть peak multiplier.
- Не разделить impression и click events.
Как сказать на собеседовании
- Всегда начинай с QPS/events/sec и latency requirement.
- Пиши формулу вслух: 200B / 86400 ~= 2.3M/sec.
High-level pipeline для CTR dashboard
Как разложить realtime CTR dashboard на ingestion, stream aggregation, storage и API?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Типовая схема: collectors -> durable log -> stream aggregation by campaign/window -> OLAP/time-series storage -> API/dashboard.
Подробный разбор
Сырой backend принимает events и пишет их в durable log: Kafka/Pulsar или аналог. Stream job читает события, группирует по campaign_id и time window, считает impressions/clicks и пишет агрегаты в хранилище для быстрых аналитических запросов.
Dashboard не должен читать сырые события на каждый refresh. Он ходит в API, которое читает materialized aggregates по campaign_id и диапазону времени, выбирает нужную гранулярность и возвращает готовый ряд clicks, impressions и CTR.
Минутные счетчики для CTR
Какие агрегаты считать по campaign_id и минутному окну, чтобы строить CTR график?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Для каждого campaign_id и window_start хранить impressions, clicks и производный CTR; CTR лучше считать из счетчиков, а не суммировать как среднее.
Подробный разбор
Основная materialized строка: campaign_id, window_start, window_size, impressions_count, clicks_count. CTR вычисляется как clicks_count / impressions_count с явной обработкой нулевого знаменателя.
Хранить именно счетчики полезнее, чем только CTR: можно пересчитать большие окна, объединять серии, проверять качество и строить confidence/volume-aware визуализацию. Для разных granularities можно либо roll up из минут, либо писать несколько materialized таблиц.
Kafka, S3 и ClickHouse: роли компонентов
В realtime CTR dashboard есть поток impression/click events. Как разделить роли Kafka, S3 и ClickHouse?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Kafka нужен как event log, S3 - для дешевого долговременного raw/cold хранения, ClickHouse - для быстрых аналитических запросов по агрегатам.
Подробный разбор
Kafka не должен быть основной базой для dashboard: он держит поток, дает buffering, replay и разделение producers/consumers. S3 полезен для дешевого архива raw events, backfill, аудита и переобработки исторических данных.
ClickHouse хранит агрегированные counters по campaign_id и time bucket, чтобы API быстро читал временные ряды без скана сырых событий. Для dashboard обычно хранят clicks и impressions отдельно, а CTR считают как derived ratio, чтобы корректно агрегировать окна.
Схема события в Kafka для ads CTR
Какая минимальная схема события нужна в Kafka для подсчета CTR кампаний?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Нужны event_id, event_time, campaign/ad/account id, event_type, user/request context и schema version; key обычно привязывают к campaign/account.
Подробный разбор
Минимальная схема должна позволять дедуплицировать, партиционировать и агрегировать событие: event_id, event_time/ingest_time, campaign_id, advertiser/account_id, event_type = impression/click, ad_id и schema_version.
Дополнительный контекст зависит от продукта: placement, country, device, experiment_id. Важно явно разделять event_time и ingest_time, потому что late events влияют на окна. Schema evolution лучше делать через registry или совместимые версии, иначе stream job будет ломаться на изменениях producer.
Какие события класть в Kafka и как партиционировать
Для realtime CTR dashboard нужно описать Kafka/event log. Какая схема события нужна и по какому ключу партиционировать?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
События: impression/click с event_id, campaign_id, ad_id, advertiser_id, timestamp, dimensions. Для агрегации CTR ключом часто делают campaign_id или campaign_id+time bucket.
Подробный разбор
В Kafka кладем immutable events: impression и click. Минимальная схема: event_id, event_type, timestamp, campaign_id, ad_id, advertiser_id, user/session id в безопасной форме, placement, region/device, price/cost при необходимости.
Ключ партиционирования зависит от агрегации. Если dashboard в основном строит CTR по campaign_id, полезно партиционировать по campaign_id, чтобы события одной кампании попадали в один поток обработки или по крайней мере стабильно распределялись. Но при hot campaigns может понадобиться salting: campaign_id + shard.
Нужно обсудить порядок, дубликаты и late events. Event_id помогает дедупликации, event time нужен для правильных окон, ingestion time - для мониторинга задержки. Click может прийти после impression с задержкой, поэтому stream job должен учитывать watermark/allowed lateness.
Типичные ошибки
- Сказать "кладем логи" без схемы события.
- Партиционировать случайно и потом делать глобальный shuffle.
- Не обсудить late events и deduplication.
Как сказать на собеседовании
- Назови схему impression/click события.
- Скажи про campaign_id как ключ и hot campaign salting.
Spark/Flink job для оконной CTR агрегации
Как должен выглядеть stream job, который считает CTR по campaign_id и временным окнам?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Job читает Kafka, watermark-ит event time, группирует по campaign_id и window, считает clicks/impressions, пишет idempotent aggregates.
Подробный разбор
Stream job читает события, валидирует schema, дедуплицирует event_id при необходимости и назначает event-time windows с watermark для late arrivals. Затем идет group by campaign_id, window_start/window_end и count по event_type.
На выходе пишется агрегат clicks/impressions/ctr в OLAP storage. Запись должна быть idempotent или transaction-like: при restart job не должен удвоить счетчики. Для незакрытых окон можно показывать preliminary values с пометкой freshness, а финализировать после watermark.
Как агрегировать CTR по минутам и где хранить результат
Как должен выглядеть stream job для CTR dashboard: что он читает, что считает и куда пишет результат для графика рекламодателя?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Stream job читает impression/click, группирует по campaign_id и time bucket, считает impressions/clicks/CTR, пишет агрегаты в OLAP storage вроде ClickHouse для dashboard API.
Подробный разбор
Stream processing слой читает события из Kafka, валидирует схему, дедуплицирует по event_id, назначает event-time bucket и агрегирует counters: impressions, clicks, возможно spend и другие метрики.
Ключ агрегации: campaign_id + bucket_start + dimensions, если нужны разрезы по placement, region или device. CTR лучше хранить не только как ratio, а как числитель и знаменатель: clicks и impressions. Тогда можно корректно пересчитывать CTR на больших окнах.
Результат пишется в хранилище для аналитических запросов: ClickHouse/Druid/Pinot/Timescale в зависимости от требований. Dashboard API читает уже агрегированные ряды, а не сырые события. Для свежих данных можно хранить minute buckets, а для истории компактизировать в 5-minute/hour/day buckets.
Типичные ошибки
- Пытаться читать сырые события из dashboard.
- Хранить только CTR и потерять clicks/impressions.
- Не учесть late events и backfill/compaction.
Как сказать на собеседовании
- Опиши pipeline по слоям.
- Скажи, что CTR хранится как clicks/impressions, а не только ratio.
API и bucket sizes для CTR dashboard
Dashboard должен показывать CTR за минуты, 5 минут и часы. Как спроектировать API и хранение bucket-агрегатов?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
API читает агрегаты по campaign_id, диапазону времени и bucket size. Лучше хранить clicks/impressions и пересчитывать CTR, а не только ratio.
Подробный разбор
Запрос обычно выглядит как campaign_id + from/to + granularity. Для свежих данных можно отдавать minute buckets, для истории - 5m/hour/day rollups. Rollups можно считать заранее или on read, если объем позволяет.
Незакрытые окна нужно явно помечать или обновлять upsert-ом, иначе график будет прыгать и вводить пользователя в заблуждение. При агрегации нельзя усреднять CTR по окнам напрямую: нужно суммировать clicks и impressions, а потом делить.
Сколько Kafka/Spark workers нужно для millions events/sec
Если поток CTR dashboard - миллионы событий в секунду, как оценить число partitions/workers и где искать bottleneck?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Нужно идти от events/sec, размера события, throughput на partition/worker и peak multiplier. Основной поток обычно impressions, clicks сильно меньше.
Подробный разбор
Оценка начинается с нагрузки: средний поток переводим в peak, умножаем на размер события и делим на реалистичную пропускную способность Kafka partitions и stream workers. Для CTR почти вся нагрузка часто идет от impressions, потому что clicks составляют малую долю.
Проверять нужно не только Kafka: serialization, network, shuffle, state store, checkpointing и write throughput в OLAP могут стать bottleneck. Хороший ответ заканчивается планом load test и метриками: consumer lag, processing latency, checkpoint duration, write errors и p95 API latency.
Partitioning по campaign_id и hot-key риски
Как сделать так, чтобы события одной кампании попадали к нужному worker и корректно агрегировались?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Kafka key по campaign/account упрощает локальную агрегацию, но нужно отдельно продумать hot campaigns, skew и число partitions.
Подробный разбор
Если key = campaign_id, все события кампании попадают в одну ordered stream partition, и worker может считать локальные counters без глобального shuffle. Это простой и надежный дизайн для per-campaign dashboard.
Риск - hot campaigns: одна крупная кампания может перегрузить partition. Тогда используют более широкий key, например campaign_id + shard, с последующим merge, или отдельную обработку hot keys. Число partitions выбирают с запасом под throughput и будущий scale-out, потому что уменьшать/менять partitioning после запуска дорого.
Output record для CTR time series
Какую строку должен писать stream job в хранилище агрегатов для dashboard?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Строка: campaign_id, window_start, window_size, impressions, clicks, ctr, update_time/version; ключи должны поддерживать быстрый range query.
Подробный разбор
Базовый record содержит campaign_id, window_start, window_end или bucket_size, impressions_count, clicks_count, ctr, last_update_time и версию/статус окна. Для ClickHouse/OLAP важно выбрать сортировку по campaign_id и времени, чтобы dashboard быстро читал диапазон.
Если нужны 1m/5m/1h графики, можно хранить отдельные rollup таблицы или строить rollup из минутных buckets. CTR лучше пересчитывать из clicks/impressions на выбранном уровне, чтобы избежать ошибки усреднения процентов.
CTR по минутным окнам для рекламных кампаний
Есть поток рекламных событий, сохраненный в таблице:
CREATE TABLE ad_events (
event_time TEXT NOT NULL,
campaign_id INTEGER NOT NULL,
event_type TEXT NOT NULL CHECK (event_type IN ('impression', 'click')),
event_id TEXT NOT NULL
);
Нужно посчитать агрегаты по минутным окнам и рекламным кампаниям.
Верните:
campaign_id;window_start— начало минуты в форматеYYYY-MM-DD HH:MM:00;impressions;clicks;ctr = clicks / impressions, округленный до 4 знаков черезROUND(..., 4).
Если в окне есть клики, но нет показов, ctr должен быть NULL.
Отсортируйте результат по campaign_id ASC, window_start ASC.
Решение прямо на странице
Напишите код, запустите проверки и только потом открывайте разбор.
Нажмите «Запустить проверки» или Ctrl+Enter.
Показать разбор
Подсказки
- Минутный bucket
В SQLite используйте strftime('%Y-%m-%d %H:%M:00', event_time).
- CTR без деления на ноль
NULLIF(impressions, 0) вернет NULL для окон без показов.
Идея решения
Минутный bucket в SQLite удобно получить через:
strftime('%Y-%m-%d %H:%M:00', event_time).
Дальше считаем условные суммы по типам событий. Для CTR используем NULLIF(impressions, 0), чтобы не делить на ноль: если показов нет, результат будет NULL.
Эталонный код
SELECT
campaign_id,
strftime('%Y-%m-%d %H:%M:00', event_time) AS window_start,
SUM(CASE WHEN event_type = 'impression' THEN 1 ELSE 0 END) AS impressions,
SUM(CASE WHEN event_type = 'click' THEN 1 ELSE 0 END) AS clicks,
ROUND(
1.0 * SUM(CASE WHEN event_type = 'click' THEN 1 ELSE 0 END) /
NULLIF(SUM(CASE WHEN event_type = 'impression' THEN 1 ELSE 0 END), 0),
4
) AS ctr
FROM ad_events
GROUP BY campaign_id, window_start
ORDER BY campaign_id ASC, window_start ASC;Checkpointing, offsets и partial windows
Stream job считает CTR по окнам и падает посередине часа. Как не потерять данные и не задвоить агрегаты?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Нужны checkpoint state, контроль Kafka offsets и идемпотентная запись агрегатов по ключу campaign_id + window_start.
Подробный разбор
Job должен восстанавливать state окон и offsets из checkpoint. Запись в storage лучше делать идемпотентной: upsert/replace по ключу окна или commit protocol, где offset продвигается только после успешной записи.
Для late events нужны watermark и политика пересчета закрытых окон. Если dashboard показывает partial windows, это должно быть явно видно: иначе пользователь будет интерпретировать недосчитанное окно как падение CTR.