LOOV Data Platform: near real-time без real-time

Как мы строили near-real-time DWH на ClickHouse + Debezium + dbt для ритейл и медтех на 120+ человек. Почему я давил на «не real-time», и где поздний владелец сущности стоил мне пяти недель пересборки.

«Real-time» почти никогда не настоящий запрос. Настоящий звучит так: «свежо настолько, что решение принимается здесь, а не в 9 утра завтра». В 90% случаев это пять минут разницы по задержке и километр разницы по стоимости.

Платформа данных окупается, когда перестаёт быть отчётным слоем и становится постоянным бизнес-сигналом. В LOOV сдвиг произошёл к седьмому месяцу. Архитектура нас туда довезла, но архитектура была менее интересной, чем разговор, который её родил.

Почему я давил на «не real-time»

Первый запрос пришёл на стендапе во второй месяц. Аня, наш руководитель аналитики, сказала: «нам нужны real-time дашборды». Я попросил пройти один пример вместе.

Взяли очевидный: текущий объём заказов на ритейл-точке. Я спросил, кто смотрит, с какой частотой, какое действие в его власти. Стор-менеджер, раз в 30 минут, может перевести второго сотрудника на кассу из другой роли. Нарисовали цикл принятия решения на доске. Действие занимает минут 8. Частота взгляда: 30 минут. Решение упирается в ёмкость менеджера, а не в задержку данных.

Остановились на near-real-time DWH, склеенном из операционных источников. Апдейты прилетают за единицы минут для того, что этого требует. Всё остальное живёт в часовых и суточных окнах. Стек: ClickHouse для аналитического слоя, Debezium для CDC из Postgres и CRM, dbt для трансформов, Metabase для дашбордов. Стоимость стека на нашем объёме примерно на порядок ниже, чем у пайплайна с push-доставкой, операционная сложность вдвое ниже. Команда построила всё без дежурства по стрим-процессингу.

Второй эффект: команда данных не стала бутылочным горлышком. На near-real-time можно итерировать модель, не координируясь вокруг стримового джоба. На восьмой неделе мы добавили шесть новых таблиц, и никто не потерял сон.

Customer 360 как продукт, а не таблица

Второе крупное решение: профили клиентов и сегментацию мы держали как полноценный внутренний продукт, а не как боковую таблицу в хранилище.

«Customer 360», написанный как SQL-представление, это снимок. Будет полезен два месяца, потом начнёт дрейфовать, потом тихо вводить в заблуждение. Мы построили его как доменную сущность с версионируемыми атрибутами, командой-владельцем, контрактом, журналом изменений и дашбордами, мониторящими здоровье самого объекта (доля null, покрытие атрибуции, дрейф сегментации). Версионирование шло по упрощённому semver: минорная версия для аддитивных атрибутов, мажорная для переименований и сдвигов семантики. Мажорное повышение требовало письменной подписи от каждой потребляющей команды.

Цена этой дисциплины окупилась сразу. Мобильное приложение хотело сегментацию для персонализированных триггеров лояльности. Вместо своего соединения подключились к контракту. Внутренние инструменты CRM хотели ту же сегментацию: тот же контракт, никаких форков. За квартал «клиент» перестал быть тремя разными вещами в трёх местах и стал одной вещью в одном.

Эту часть, по моим ощущениям, недооценивают. Большинство разговоров про платформу данных идёт про пайплайны и стек. Решение, которое в итоге стоит больше всего, такое: канонические сущности (клиент, заказ, точка, сотрудник) у вас продукт или они побочный результат запросов.

Где я заплатил за позднего владельца

Дорогая ошибка.

Customer 360 мы взяли во владение и закрыли контрактом с первой недели. События заказа: нет. Пайплайн заказов рос органически. Каждая команда-потребитель, которой нужны были данные о заказах, делала свои соединения по Bitrix и легаси. За четыре месяца накопилось пять дашбордов, построенных на чуть разных определениях «заказ выполнен». Когда компания поменяла, как возврат влияет на статус заказа, три из этих дашбордов показали правильное число, два показали старое, и команда лидерства неделю не верила ни одному.

В девятом месяце мы заморозили пайплайн заказов, сели с командами, которые им неявно владели, и переписали контракт с нуля. Пять недель работы инженеров, реальная стоимость. Коммерция жила на ручной выгрузке в Гугл-таблицах все эти пять недель.

Урок не «делайте контракты на всё». Он такой: в любой момент времени небольшое число сущностей вот-вот станут каноническими, хотите вы этого или нет. «Заказ» уже был каноническим к четвёртому месяцу. Я заметил это в девятом. Четыре месяца дрейфа оплачены пятью неделями пересборки. Если бы делал тот же проект сейчас, заложил бы два дня в квартал на вопрос: «какая сущность тихо стала канонической, а я с ней так не обращаюсь?»

Где я перегнул

Линию «никакого real-time» держал слишком жёстко. Нашлось 5-10 конкретных решений в операционке, где 30-минутная задержка была реально медленной, и мы их закатали в общий шаблон. Во второй год построили под них небольшой стримовый обходной канал, и это было правильно. Вывод не «использовать стриминг» и не «не использовать стриминг». Он такой: рисуй цикл принятия решения до того, как выбираешь бюджет задержки. Я в первый год рисовал недостаточно часто.

Что в итоге

Сегодня платформа: near-real-time DWH для того, что этого требует, пакетная обработка для остального. Доменные сущности (клиент, заказ, точка) поддерживаются как продукты с контрактами. Дашборды принадлежат потребителям и удаляются агрессивно. Аня сейчас владелец продукта данных. Аналитик из CRM-истории во второй год переехал к ней и на первой неделе сел писать контракт для сущности «точка».

Это единственная метрика, которой я доверяю для такой платформы: новый человек начинает с написания контракта или с написания запроса? Если контракта, платформа делает свою работу.