Сборка identity_map: трансформация и повторы #133

Closed
opened 2026-08-22 10:26:37 +03:00 by ddmitry · 1 comment
Owner

Part of #127.

Вопрос

Механика сборки карты кука↔пользователь: из чего читается мост
(purchase-событие и заказ), как выглядит трансформация с GLOBAL JOIN,
идемпотентность и повторный прогон — как при ReplacingMergeTree не
переписать first_matched_at первого совпадения, — и когда сборка
запускается.

Схема, движок и шардирование решены спекой §5 и не пересматриваются:
dds.identity_map (client_id, user_id, first_matched_at),
ReplacingMergeTree, ORDER BY (client_id, user_id), шардирование
cityHash64(client_id).

Сначала прочитать

  • мастер-спека, раздел 5;
  • CONTEXT.md: «Человек», «Посетитель», «Пользователь магазина»;
  • резолюция research-тикета о GLOBAL JOIN (блокер этого тикета).
Part of #127. ## Вопрос Механика сборки карты кука↔пользователь: из чего читается мост (`purchase`-событие и заказ), как выглядит трансформация с GLOBAL JOIN, идемпотентность и повторный прогон — как при ReplacingMergeTree не переписать `first_matched_at` первого совпадения, — и когда сборка запускается. Схема, движок и шардирование решены спекой §5 и не пересматриваются: `dds.identity_map` (client_id, user_id, first_matched_at), ReplacingMergeTree, ORDER BY (client_id, user_id), шардирование `cityHash64(client_id)`. ## Сначала прочитать - мастер-спека, раздел 5; - `CONTEXT.md`: «Человек», «Посетитель», «Пользователь магазина»; - резолюция research-тикета о GLOBAL JOIN (блокер этого тикета).
ddmitry added the wayfinder:grilling label 2026-08-22 10:26:39 +03:00
ddmitry self-assigned this 2026-08-24 21:18:01 +03:00
Author
Owner

Резолюция

Карта — физическая пакетная модель DDS, а не динамическое соединение на каждом чтении.

  • Хранение: dds.identity_map_rep на ReplicatedReplacingMergeTree(_load_ts), ключ (client_id, user_id); dds.identity_map_dist шардирует по cityHash64(client_id).
  • Публичный интерфейс: dds.identity_map_v с явным списком колонок и FINAL. Попутно закреплено общее правило DDS: потребительские роли и витрины читают *_v; *_rep и *_dist остаются загрузке, диагностике и обслуживанию. Поэтому публичные поверхности получают также заказ и сессия.
  • Граница доступа: обычные DDS-представления создаются с DEFINER = CURRENT_USER SQL SECURITY DEFINER; потребитель получает SELECT на _v, а запрос к _rep и _dist отклоняется.
  • Деловые поля карты — только client_id и user_id; собственные координаты запуска — _load_id, _load_ts. first_matched_at удалён: запланированного читателя нет, а два возможных смысла времени требуют разных исторических моделей. Идея вынесена в #145.
  • Источники: покупки из всей доступной истории dds.event_v и текущее состояние dds.order_v. Любой сопоставленный заказ устанавливает личность независимо от статуса.
  • Сборка: суточный батч после обновления заказов, GLOBAL INNER JOIN по purchase_id = order_id, затем различные пары. Для догоняющей пачки — один запуск после всех заказов.
  • Повтор: каждый запуск пишет полный текущий набор пар с новой версией загрузки. Предыдущая карта не читается, anti-join не используется; точность чтения даёт FINAL в публичном представлении.
  • Словарь и одно динамическое представление без физической модели отклонены: нового урока не дают и скрывают реалистичное устройство слоя.

Решение записано в docs/architecture/dds/identity.md, карте DDS, документах заказа и сессии, конвенциях хранилища и мастер-спеке.

## Резолюция Карта — физическая пакетная модель DDS, а не динамическое соединение на каждом чтении. - Хранение: `dds.identity_map_rep` на `ReplicatedReplacingMergeTree(_load_ts)`, ключ `(client_id, user_id)`; `dds.identity_map_dist` шардирует по `cityHash64(client_id)`. - Публичный интерфейс: `dds.identity_map_v` с явным списком колонок и `FINAL`. Попутно закреплено общее правило DDS: потребительские роли и витрины читают `*_v`; `*_rep` и `*_dist` остаются загрузке, диагностике и обслуживанию. Поэтому публичные поверхности получают также заказ и сессия. - Граница доступа: обычные DDS-представления создаются с `DEFINER = CURRENT_USER SQL SECURITY DEFINER`; потребитель получает `SELECT` на `_v`, а запрос к `_rep` и `_dist` отклоняется. - Деловые поля карты — только `client_id` и `user_id`; собственные координаты запуска — `_load_id`, `_load_ts`. `first_matched_at` удалён: запланированного читателя нет, а два возможных смысла времени требуют разных исторических моделей. Идея вынесена в #145. - Источники: покупки из всей доступной истории `dds.event_v` и текущее состояние `dds.order_v`. Любой сопоставленный заказ устанавливает личность независимо от статуса. - Сборка: суточный батч после обновления заказов, `GLOBAL INNER JOIN` по `purchase_id = order_id`, затем различные пары. Для догоняющей пачки — один запуск после всех заказов. - Повтор: каждый запуск пишет полный текущий набор пар с новой версией загрузки. Предыдущая карта не читается, anti-join не используется; точность чтения даёт `FINAL` в публичном представлении. - Словарь и одно динамическое представление без физической модели отклонены: нового урока не дают и скрывают реалистичное устройство слоя. Решение записано в `docs/architecture/dds/identity.md`, карте DDS, документах заказа и сессии, конвенциях хранилища и мастер-спеке.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#133