Ко-локация заказов в ODS сломана: заказы задвоены по шардам #137

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

Найдено research-тикетом #129 (заметка
docs/research/2026-08-22-distributed-join.md, ветка
research/distributed-join) — только чтением, стенд не менялся.

Находка и причина

Заказы в ODS не разложены по своему ключу шардирования: 1189 из 1662
заказов лежат на обоих шардах одинаковыми строками, count() по
ods.order_dist даёт 2851 вместо 1662. События целы: все строки
ods.event разложены по cityHash64(ClientID).

Причина — заводское parallel_distributed_insert_select = 2: обе
вставки sql/ods/order_parse.sql (INSERT INTO ods.order_dist … FROM stg.orders_raw_dist) выполняются локально на каждом шарде, минуя ключ
шардирования цели (cityHash64(order_id)). Слепок переотправляет заказ
каждый день окна изменяемости, сырьё день ото дня ложится по-разному —
и копии одного заказа расползаются по шардам, где ReplacingMergeTree
через границу шарда их не схлопывает.

Цель

Заказы в ODS лежат каждый на своём шарде по cityHash64(order_id), и
шаг разбора не воспроизводит дефект на следующих слепках.

Порядок

  1. Сначала механизм. Сверить семантику значений
    parallel_distributed_insert_select через MCP Context7 и выбрать то,
    при котором инициатор раскладывает строки по ключу шардирования цели
    (ожидаемо 0). Добавить настройку в SETTINGS обеих вставок
    sql/ods/order_parse.sql с комментарием-доводом.
  2. Потом данные. Адресная очистка: TRUNCATE локальных
    ods.order и ods.order_errors ON CLUSTER (make clean не нужен:
    состояние — только эти две таблицы; сырьё в STG не трогать — оно
    точка повтора). Затем повторный разбор всех порций тем же шагом: по
    списку SELECT DISTINCT _load_id FROM stg.orders_raw_dist.
    Перед очисткой убедиться, что сырьё полно (срок жизни сырья ещё не
    съел ранние порции — storage.md, «Срок жизни сырья»); если нет —
    вместо п. 2 make rebuild-storage с переигрыванием мира.
  3. Прожить следующий модельный день и повторить проверку раскладки.

Критерии приёмки

  • Семантика parallel_distributed_insert_select сверена (Context7);
    значение и довод — комментарием у обеих вставок
    sql/ods/order_parse.sql.
  • docs/architecture/storage.md, «Раскладка по шардам», тем же PR:
    описан капкан «INSERT из Distributed в Distributed при
    несовпадающих ключах» — он ждёт и будущие сборки DDS
    (развилки #130 и #134 карты #127).
  • Заказы и брак переложены: проверочный запрос ниже даёт 0;
    uniqExact(order_id) по ods.order_dist не изменился после
    перекладки.
  • После приёма следующего слепка проверка раскладки снова даёт 0.

Границы

  • DDL не менять: ключи шардирования *_dist верны, дефект в исполнении
    вставки.
  • События (ods.event) не трогать — целы.
  • Общий способ загрузки DDS здесь не решать — это развилки #130 и Запуск трансформаций до etl_pipeline (#134)
    карты #127; сюда только заказы и предупреждение в доке.
  • Сырьё STG не чистить и не перекладывать: его раскладка — свойство
    доставки, читатель среза фильтрует по _load_id.

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

  • docs/research/2026-08-22-distributed-join.md — запросы и цифры;
  • docs/architecture/storage.md — «Раскладка по шардам», «Срок жизни
    сырья»;
  • sql/ods/order_parse.sql, dags/orders_ingest.py — шаг разбора и
    кто его запускает;
  • ADR 0008 (приём заказов), ADR 0010 (версии заказов в ODS).

Команды проверки

Клиент ClickHouse — по ADR 0007. Межшардовые дубли (ожидается 0):

SELECT countIf(shards > 1) AS mislaid
FROM (
    SELECT order_id, uniqExact(hostName()) AS shards
    FROM cluster('clickstream_cluster', ods.order)
    GROUP BY order_id
);

Сохранность вершины версий до/после перекладки:

SELECT uniqExact(order_id) FROM ods.order_dist;
SELECT count() FROM ods.order_v;

Стендовые цели карты проверок остаются зелёными: make smoke,
make check-clickhouse.

Найдено research-тикетом #129 (заметка `docs/research/2026-08-22-distributed-join.md`, ветка `research/distributed-join`) — только чтением, стенд не менялся. ## Находка и причина Заказы в ODS не разложены по своему ключу шардирования: 1189 из 1662 заказов лежат на обоих шардах одинаковыми строками, `count()` по `ods.order_dist` даёт 2851 вместо 1662. События целы: все строки `ods.event` разложены по `cityHash64(ClientID)`. Причина — заводское `parallel_distributed_insert_select = 2`: обе вставки `sql/ods/order_parse.sql` (`INSERT INTO ods.order_dist … FROM stg.orders_raw_dist`) выполняются локально на каждом шарде, минуя ключ шардирования цели (`cityHash64(order_id)`). Слепок переотправляет заказ каждый день окна изменяемости, сырьё день ото дня ложится по-разному — и копии одного заказа расползаются по шардам, где `ReplacingMergeTree` через границу шарда их не схлопывает. ## Цель Заказы в ODS лежат каждый на своём шарде по `cityHash64(order_id)`, и шаг разбора не воспроизводит дефект на следующих слепках. ## Порядок 1. **Сначала механизм.** Сверить семантику значений `parallel_distributed_insert_select` через MCP Context7 и выбрать то, при котором инициатор раскладывает строки по ключу шардирования цели (ожидаемо `0`). Добавить настройку в `SETTINGS` обеих вставок `sql/ods/order_parse.sql` с комментарием-доводом. 2. **Потом данные.** Адресная очистка: `TRUNCATE` локальных `ods.order` и `ods.order_errors` ON CLUSTER (`make clean` не нужен: состояние — только эти две таблицы; сырьё в STG не трогать — оно точка повтора). Затем повторный разбор всех порций тем же шагом: по списку `SELECT DISTINCT _load_id FROM stg.orders_raw_dist`. Перед очисткой убедиться, что сырьё полно (срок жизни сырья ещё не съел ранние порции — `storage.md`, «Срок жизни сырья»); если нет — вместо п. 2 `make rebuild-storage` с переигрыванием мира. 3. Прожить следующий модельный день и повторить проверку раскладки. ## Критерии приёмки - [x] Семантика `parallel_distributed_insert_select` сверена (Context7); значение и довод — комментарием у обеих вставок `sql/ods/order_parse.sql`. - [x] `docs/architecture/storage.md`, «Раскладка по шардам», тем же PR: описан капкан «INSERT из Distributed в Distributed при несовпадающих ключах» — он ждёт и будущие сборки DDS (развилки #130 и #134 карты #127). - [x] Заказы и брак переложены: проверочный запрос ниже даёт 0; `uniqExact(order_id)` по `ods.order_dist` не изменился после перекладки. - [x] После приёма следующего слепка проверка раскладки снова даёт 0. ## Границы - DDL не менять: ключи шардирования `*_dist` верны, дефект в исполнении вставки. - События (`ods.event`) не трогать — целы. - Общий способ загрузки DDS здесь не решать — это развилки #130 и #134 карты #127; сюда только заказы и предупреждение в доке. - Сырьё STG не чистить и не перекладывать: его раскладка — свойство доставки, читатель среза фильтрует по `_load_id`. ## Сначала прочитать - `docs/research/2026-08-22-distributed-join.md` — запросы и цифры; - `docs/architecture/storage.md` — «Раскладка по шардам», «Срок жизни сырья»; - `sql/ods/order_parse.sql`, `dags/orders_ingest.py` — шаг разбора и кто его запускает; - ADR 0008 (приём заказов), ADR 0010 (версии заказов в ODS). ## Команды проверки Клиент ClickHouse — по ADR 0007. Межшардовые дубли (ожидается 0): ```sql SELECT countIf(shards > 1) AS mislaid FROM ( SELECT order_id, uniqExact(hostName()) AS shards FROM cluster('clickstream_cluster', ods.order) GROUP BY order_id ); ``` Сохранность вершины версий до/после перекладки: ```sql SELECT uniqExact(order_id) FROM ods.order_dist; SELECT count() FROM ods.order_v; ``` Стендовые цели карты проверок остаются зелёными: `make smoke`, `make check-clickhouse`.
ddmitry added the needs-triage label 2026-08-22 10:40:56 +03:00
ddmitry added ready-for-agent and removed needs-triage labels 2026-08-22 10:48:43 +03:00
ddmitry self-assigned this 2026-08-22 11:29:17 +03:00
Author
Owner

Влито в main PR #138 (544e500). Все критерии приёмки закрыты: настройка сверена и закомментирована у обеих вставок, капкан описан в storage.md, заказы переложены (межшардовые дубли 0, uniqExact не изменился), после следующего слепка проверка снова даёт 0. Замеры — в «Что проверено» storage.md и orders/ingestion.md.

Влито в main PR #138 (544e500). Все критерии приёмки закрыты: настройка сверена и закомментирована у обеих вставок, капкан описан в storage.md, заказы переложены (межшардовые дубли 0, uniqExact не изменился), после следующего слепка проверка снова даёт 0. Замеры — в «Что проверено» storage.md и orders/ingestion.md.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ddmitry/clickstream-data-platform#137