В отчёте дневная конверсия регистрации в покупку занижена для ранних дат. Какой механизм создаёт ошибку в этом запросе?
WITH events(user_id, event_name, event_time, loaded_at) AS (
VALUES
(1, 'signup', TIMESTAMP '2025-05-01 10:00', TIMESTAMP '2025-05-01 10:01'),
(1, 'purchase', TIMESTAMP '2025-05-02 12:00', TIMESTAMP '2025-05-04 09:00'),
(2, 'signup', TIMESTAMP '2025-05-01 11:00', TIMESTAMP '2025-05-01 11:01'),
(2, 'purchase', TIMESTAMP '2025-05-01 12:00', TIMESTAMP '2025-05-01 12:01')
)
SELECT s.loaded_at::date,
COUNT(p.user_id) * 1.0 / COUNT(*) AS conversion
FROM events s
LEFT JOIN events p
ON p.user_id = s.user_id
AND p.event_name = 'purchase'
AND p.loaded_at::date = s.loaded_at::date
WHERE s.event_name = 'signup'
GROUP BY s.loaded_at::date;
Ошибка возникает из-за смешения времени события и времени загрузки события в хранилище. Покупка пользователя 1 произошла 2 мая, но была загружена 4 мая, поэтому запрос не засчитывает её в конверсию регистрации 1 мая.
Когорту и окно конверсии нужно строить по event_time, а loaded_at использовать для контроля полноты данных и задержки доставки. Иначе поздние события искусственно занижают ранние даты и могут создавать ложную динамику продукта.
Событийная аналитика обычно собирает данные из мобильных приложений, веб-клиентов и внешних систем. Эти источники не гарантируют, что событие попадёт в хранилище сразу после фактического действия пользователя: возможны офлайн-режим, повторы отправки, очереди и задержки обработки.
Поэтому в аналитических системах различают event time — когда действие произошло, и processing time или время загрузки — когда запись стала доступна для анализа. Это разделение появилось как практический способ не путать поведение пользователя с работой конвейера данных.
Если группировать регистрации и покупки по loaded_at, отчёт измеряет не пользовательскую конверсию, а совпадение дат поступления событий. Чем позже событие покупки доставлено, тем сильнее занижается конверсия регистрации в ранних периодах.
Такая ошибка особенно опасна для свежих дат, каналов с нестабильной доставкой событий и мобильных пользователей, которые долго находятся офлайн. Команда может ошибочно решить, что после релиза или рекламной кампании конверсия упала, хотя изменилось только время поступления данных.
Сначала нужно определить аналитическую сущность: например, конверсию пользователей, зарегистрировавшихся в день D, в течение семи дней после регистрации. Регистрация задаёт когорту по event_time, а покупка засчитывается, если её event_time попадает в заданное окно.
В реальном отчёте также нужно дедуплицировать события и явно определить, считается ли первая покупка, любая покупка или уникальный пользователь с хотя бы одной покупкой. Метрика должна иметь фиксированное окно атрибуции; сравнивать незрелую когорту с полностью созревшей некорректно.
loaded_at не следует игнорировать. По нему полезно строить контроль задержки доставки, долю поздних событий и дату готовности когорты. Например, данные за вчера могут быть технически неполными, поэтому отчёт либо публикуют после контрольного лага, либо помечают как предварительный и пересчитывают после дозагрузки.
Есть компромисс между оперативностью и стабильностью. Ожидание полной дозагрузки повышает точность, но задерживает отчёт; ранняя публикация ускоряет реакцию, но требует явной маркировки неполноты и последующих пересчётов.
У сервиса доставки после запуска новой версии дневная конверсия регистрации в первый заказ снизилась с 18% до 14%. При проверке выяснилось, что пользователи новой версии чаще оформляли заказ при нестабильном соединении, а события покупки загружались с задержкой до двух дней.
Команда рассматривала два варианта. Первый — считать конверсию по дате загрузки: это позволяло быстро получать отчёт, но смешивало поведение пользователей с задержками доставки. Второй — считать когорты и заказы по времени события, а свежие когорты временно исключать до завершения семидневного окна; этот вариант был медленнее, зато сохранял смысл метрики.
Выбрали второй вариант и добавили отдельный мониторинг лага между event_time и loaded_at. После дозагрузки данных конверсия вернулась к прежнему уровню, а исходное падение оказалось артефактом неполных когорт, а не ухудшением продукта.
loaded_at на event_time?Нет. Нужно также определить окно конверсии и проверить полноту данных. Если покупка может произойти в течение семи дней, когорту первого мая нельзя окончательно сравнивать с когортой пятнадцатого мая до завершения соответствующего окна.
Нужно поддержать пересчёт исторических периодов или корректировки. Практически применяют водяной знак, задержку публикации и статус зрелости данных; простое игнорирование позднего события оставляет систематическое занижение метрики.
Да, если задержка вызвана изменением клиентского поведения или надёжности отправки событий после релиза. Поэтому нужно разделять два вывода: продуктовую метрику считать по времени события, а качество аналитического конвейера оценивать по задержке, потерям, дублям и доле поздних записей.