docs(ingestion): упрощены объяснения источников и приема данных
Зачем: помочь начинающему инженеру проследить событие и версию заказа от генератора до хранилища, включая ошибки и повторные поступления. Что: обновлены вводная генератора, справочники приема и модели заказа, разделы хранилища и комментарии SQL и дага. Сохранены технические условия; исполняемый код, схемы и поведение стенда не изменены. Проверка: make lint, git diff --check, 142 локальные ссылки и справка CLI. SQL без комментариев и Python AST без строк документации совпали с исходными. Генерация данных и сброс состояния не выполнялись. Refs #250
This commit is contained in:
1 parent
83434102d5
commit
75f28fe99f
13 files changed
+663
-974
No files matched your search
+27
-68
@@ -1,49 +1,20 @@
|
||||
-- ODS: типизированное событие и таблица ошибок разбора.
|
||||
--
|
||||
-- Состав, имена и типы колонок списаны с «описания выгрузки»
|
||||
-- (docs/formats/clickstream-event.md) — так же, как в бою хранилище пишут по
|
||||
-- документации источника. Модуль контракта генератора здесь не читается:
|
||||
-- граница «трекер | хранилище» проходит по документу.
|
||||
-- Механика приёма и три класса брака — ADR 0005, конвенции имён, служебных
|
||||
-- колонок и сроков — docs/architecture/storage.md.
|
||||
|
||||
-- Локальная таблица события.
|
||||
-- Поля следуют docs/formats/clickstream-event.md; purchaseRevenue хранится
|
||||
-- в Decimal для точного счета денег. Правила приема — ADR 0005.
|
||||
-- ReplacingMergeTree оставляет последнюю доставку по _load_ts среди строк
|
||||
-- с одинаковым полным ORDER BY. Метка переносится из STG без пересчета.
|
||||
-- Она не входит в ключ сортировки или партиции: иначе повторные доставки
|
||||
-- не считались бы версиями одной строки. Для чтения служит ods.event_v с FINAL.
|
||||
--
|
||||
-- Движок ReplacingMergeTree, колонка версии — _load_ts. Работа у версии ровно
|
||||
-- одна: схлопнуть повтор доставки. Метка не ставится здесь заново, а
|
||||
-- переносится из stg.hits_raw как есть — колонка отвечает на вопрос «когда
|
||||
-- строка приехала в хранилище», а не «когда её разобрали». У повтора
|
||||
-- содержимое то же самое, отличается только метка, поэтому какая из двух
|
||||
-- строк переживёт мерж, безразлично.
|
||||
-- Партиция EventDate хранит модельный день события. CounterID на стенде
|
||||
-- постоянен, EventDate постоянна внутри партиции; дальнейшая сортировка —
|
||||
-- по intHash32(ClientID) и WatchID. WatchID отличает события посетителя:
|
||||
-- без него они заменяли бы друг друга.
|
||||
--
|
||||
-- Колонка версии не входит ни в ключ партиции, ни в ключ сортировки, и это
|
||||
-- не случайность: попади она туда — версии одной строки лягут в разные куски
|
||||
-- или в разные места ключа и не встретятся при мерже, то есть дедупликация
|
||||
-- перестанет работать молча.
|
||||
--
|
||||
-- PARTITION BY EventDate — по дню события, а не по месяцу, как у Метрики.
|
||||
-- Отступление осознанное: дневная партиция здесь единица переобработки, и
|
||||
-- переиграть день X значит заменить одну партицию. Месячная партиция тянула
|
||||
-- бы за собой тридцать чужих дней.
|
||||
--
|
||||
-- ORDER BY вырожден, и это учебный факт, а не недосмотр: CounterID на стенде
|
||||
-- константа (сайт один), EventDate константа внутри своей партиции — обе
|
||||
-- головные колонки ключа не различают ни одной строки. Реальная сортировка
|
||||
-- идёт по посетителю и событию: intHash32(ClientID), WatchID. Ключ написан в
|
||||
-- боевой форме «по сайту за период по посетителю» — на стенде она
|
||||
-- вырождается, в бою нет. Хвост WatchID работает ещё и на дедупликацию: без
|
||||
-- него ReplacingMergeTree схлопнул бы в одну строку все события посетителя за
|
||||
-- день.
|
||||
--
|
||||
-- SAMPLE BY intHash32(ClientID) — по тому же выражению, что стоит в ключе
|
||||
-- сортировки (иначе семплирование не разрешено). Семплирование берёт целиком
|
||||
-- посетителей, а не события вразнобой, поэтому SAMPLE 0.1 не портит
|
||||
-- uniq-метрики.
|
||||
--
|
||||
-- Sign всегда равен 1: это колонка формата, взятая без механики. В бою
|
||||
-- исправление записи шлют парой −1/+1 и считают через sum(Sign) поверх
|
||||
-- CollapsingMergeTree; наш генератор исправлений не шлёт, поэтому колонка
|
||||
-- есть, а механики за ней нет.
|
||||
-- SAMPLE BY использует то же выражение, что ключ сортировки, и отбирает
|
||||
-- посетителей целиком. Sign всегда равен 1: поле сохранено по формату
|
||||
-- источника, но исправлений парой −1/+1 генератор не отправляет.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ods.event_rep ON CLUSTER clickstream_cluster
|
||||
(
|
||||
WatchID UInt64,
|
||||
@@ -100,36 +71,24 @@ PARTITION BY EventDate
|
||||
ORDER BY (CounterID, EventDate, intHash32(ClientID), WatchID)
|
||||
SAMPLE BY intHash32(ClientID);
|
||||
|
||||
-- Лицо слоя: пишем и читаем через него. Ключ шардирования —
|
||||
-- cityHash64(ClientID), а не сырой ClientID: структурированный числовой
|
||||
-- идентификатор перекашивает остаток по модулю числа шардов, хеш — нет.
|
||||
-- События одной куки при этом остаются на одном шарде, и сессионизация со
|
||||
-- склейкой идентичностей ниже по течению живут локально.
|
||||
--
|
||||
-- У STG ключ другой (хеш строки), поэтому сырая строка и разобранное из неё
|
||||
-- событие почти всегда лежат на разных шардах. Пошардовые счётчики слоёв
|
||||
-- сходиться не должны — сверять слои можно только через _dist.
|
||||
-- Запись и диагностика через Distributed; актуальные события — в ods.event_v.
|
||||
-- cityHash64(ClientID) распределяет посетителей равномернее, чем остаток
|
||||
-- самого структурированного идентификатора. События посетителя лежат вместе.
|
||||
-- STG распределен по хешу raw, поэтому сообщение и разобранное событие могут
|
||||
-- лежать на разных шардах. Общие счетчики сравнивают через _dist.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ods.event_dist ON CLUSTER clickstream_cluster
|
||||
AS ods.event_rep
|
||||
ENGINE = Distributed('clickstream_cluster', 'ods', 'event_rep', cityHash64(ClientID));
|
||||
|
||||
-- Локальная таблица ошибок разбора.
|
||||
-- Ошибки разбора: исходный текст, класс и сведения о доставке.
|
||||
-- У сообщения может не быть разобранного ClientID, поэтому шардирование —
|
||||
-- по cityHash64(raw), партиция — по реальному дню загрузки.
|
||||
-- ORDER BY группирует ошибки по классу и координатам Kafka.
|
||||
--
|
||||
-- Сырой текст сообщения, метаданные доставки и класс брака. Разобранных полей
|
||||
-- у брака нет по определению: строка сюда попала как раз потому, что не
|
||||
-- разобралась.
|
||||
--
|
||||
-- Движок — обычный ReplicatedMergeTree, без замены версий: у брака нет ключа
|
||||
-- сущности, схлопывать его не по чему.
|
||||
--
|
||||
-- Ключи собственные. Шардирование — cityHash64(raw): ClientID у неразобранной
|
||||
-- строки взять неоткуда. ORDER BY (error_class, kafka_partition,
|
||||
-- kafka_offset): такую таблицу смотрят от класса брака, а внутри класса — по
|
||||
-- координатам доставки. Нарезка по дню загрузки, как у STG: модельного дня
|
||||
-- у брака тоже нет.
|
||||
--
|
||||
-- Срок жизни — месяц, вдесятеро дольше сырых данных. Истеки брак вместе с ними —
|
||||
-- к моменту разбирательства не осталось бы ни того, ни другого.
|
||||
-- Замены версий нет: надежного ключа события у брака может не быть.
|
||||
-- TTL сохраняет ошибки месяц, дольше трехсуточного срока STG.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ods.event_errors_rep ON CLUSTER clickstream_cluster
|
||||
(
|
||||
raw String,
|
||||
|
||||
Reference in new issue
Block a user