Merge pull request 'Разложить заказы в ODS по своему ключу шардирования' (#138) from 137-orders-shard-colocation into main

Reviewed-on: #138
This commit was merged in pull request #138.
This commit is contained in:
ddmitry committed 2026-08-22 11:38:55 +03:00
commit 544e500dc8
4 files changed
+57 -3

No files matched your search

+21
View File
@@ -106,6 +106,20 @@ JSON-объектом с точным набором ключей: `order_id`, `
- `_load_id` — какой запуск Airflow принял строку;
- `_load_ts` — когда строка приехала в хранилище.
Версию выбирает только `updated_at`. Заказ, не менявшийся несколько дней,
приезжает в нескольких слепках одинаковыми деловыми полями, и какая из этих
строк переживёт схлопывание — не обещано. Поэтому `snapshot_date` выжившей
строки — один из дней, когда источник показывал эту версию, не обязательно
последний; то же с `_load_id` и `_load_ts`. Вопрос «когда заказ наблюдался в
последний раз» задаётся сырью STG, пока то живо, — а не одной строке ODS.
Сама схема версий стоит на допущении об источнике: уточнение заказа поднимает
`updated_at`. Генератор моделирует источник с триггерной меткой и допущение
выполняет (замер — «Что проверено»); источник, умеющий уточнить строку тихо —
ручным `UPDATE` мимо приложения, миграцией, — ломает его, и тогда версии по
метке времени мало: нужна сверка самих полей, в терминах dbt snapshots —
стратегия `check` вместо `timestamp`.
Обычное чтение `_dist` показывает физически сохранившиеся версии и нужно для
диагностики. Их число зависит от фоновых слияний: ODS не служит архивом истории.
`ods.order_v` сохраняет те же источник-ориентированные поля без обогащения
@@ -186,6 +200,13 @@ JSON-объектом с точным набором ключей: `order_id`, `
`system.query_log` на первой ноде осталось ровно по одному завершённому запросу
пользователя `etl` для забора в STG, записи годных строк и записи брака.
Допущение и произвол ярлыков замерены 22 августа 2026 года при исполнении
#137: во всех 3034 парах строк с равным `updated_at` деловые поля совпали
побайтово, по слепкам метка ни разу не убывала, а `ods.order_v` сошёлся
построчно с эталоном `argMax` по `updated_at`, посчитанным прямо из сырья.
Произвол ярлыков виден въявь: у 86 заказов стартового мира выжила строка
серединного слепка — не первого и не последнего.
Забор из Kafka в STG снят на живом стенде 18 августа 2026 года при исполнении
#93: одно прямое чтение приносит весь слепок дня, метаданные доставки доступны,
офсеты коммитятся, а сбой забора не двигает позицию мира. Числа — [ADR
+21
View File
@@ -84,6 +84,18 @@ CSV-файла. Живёт он вне цепочки слоёв, в зоне `d
Пошардовые счётчики STG и ODS поэтому не сходятся и сходиться не должны —
сверять слои можно только через `_dist`.
Разные ключи у слоёв ставят капкан на вставке `INSERT INTO цель_dist SELECT …
FROM источник_dist`. Заводское значение `parallel_distributed_insert_select` —
2: если обе таблицы на одном кластере, вставка исполняется локально на каждом
шарде, минуя ключ шардирования цели. Строки наследуют раскладку источника, и
ошибки при этом нет — расхождение видно только пересчётом ключей по
`hostName()`. Так половина заказов и легла на оба шарда, где
`ReplacingMergeTree` через границу шарда дубли не схлопывает (замер — «Что
проверено», #137). Лекарство — явное `parallel_distributed_insert_select = 0`
в `SETTINGS` такой вставки: раскладку делает инициатор, по ключу цели
(`sql/ods/order_parse.sql`). Капкан ждёт и сборки DDS: они тоже будут читать
`_dist` и писать в `_dist` с другим ключом.
Ключи ко-локации названы заранее, потому что на них стоит политика соединений из
раздела 6 спеки: обычное соединение разрешено только по ключу ко-локации, всё
прочее — через `GLOBAL`. Значит `dds.session` и `dds.identity_map` шардируются по
@@ -573,6 +585,15 @@ DDL-словарь с файловым источником внутри `user_f
разобрано, почему отказ в праве на движок называет не то право, которого не
хватает.
**Проверка ко-локации заказов 22 августа 2026 года (#137).** Context7 и
`system.settings` закреплённого ClickHouse 26.3 назвали одну и ту же тройку
значений `parallel_distributed_insert_select`: `0` — выключено, `1` — `SELECT`
на каждом шарде, `2` — `SELECT` и `INSERT` на каждом шарде; заводское — `2`.
На живых данных видны и дефект, и починка: до правки 1189 из 1662 заказов
лежали на обоих шардах, после явного нуля и повторного разбора того же
`_load_id` — ни одного. Вершина версий уцелела: деловые поля `ods.order_v`
сошлись с эталоном, посчитанным прямо из сырья.
**Проверка версий заказов 18 августа 2026 года (#94).** MCP Context7 подтвердил,
что `JSONType` возвращает имя типа значения JSON, — на нём стоит проверка типов
в предикате приёма заказов. Остальное снято на закреплённом ClickHouse 26.3.
+4 -1
View File
@@ -163,7 +163,10 @@ ENGINE = Distributed('clickstream_cluster', 'ods', 'event_errors_rep', cityHash6
-- updated_at — какая бизнес-версия новее; snapshot_date — в слепке какого
-- модельного дня источник показал строку; _load_id — какой запуск Airflow её
-- принял; _load_ts — когда она приехала в хранилище. Ключом сущности не
-- становится ни одна из трёх последних: они про наблюдение и загрузку.
-- становится ни одна из трёх последних: они про наблюдение и загрузку. Версию
-- выбирает только updated_at, поэтому у выжившей после схлопывания строки
-- snapshot_date — один из дней, когда источник показывал эту версию, не
-- обязательно последний (docs/architecture/orders/ingestion.md).
--
-- PARTITION BY toDate(created_at) — по дню создания строки в источнике. Он
-- неизменен у всех версий заказа, поэтому версии лежат в одной партиции и
+11 -2
View File
@@ -92,7 +92,13 @@ SELECT
_load_ts
FROM stg.orders_raw_dist
WHERE _load_id = {load_id:String} AND row_is_valid
SETTINGS distributed_foreground_insert = 1;
-- Заводская двойка parallel_distributed_insert_select исполняет вставку из
-- Distributed в Distributed локально на каждом шарде, минуя ключ шардирования
-- цели, — так заказы и расползлись по обоим шардам. Ноль отдаёт раскладку
-- инициатору, по ключу цели. Капкан целиком — docs/architecture/storage.md,
-- «Раскладка по шардам».
SETTINGS distributed_foreground_insert = 1,
parallel_distributed_insert_select = 0;
-- Брак: тот же срез и буквальное отрицание того же предиката.
--
@@ -118,4 +124,7 @@ SELECT
_load_ts
FROM stg.orders_raw_dist
WHERE _load_id = {load_id:String} AND NOT row_is_valid
SETTINGS distributed_foreground_insert = 1;
-- Тот же ноль: ключ брака сегодня совпадает с ключом сырья, и локальная
-- запись легла бы верно — но по совпадению, а не по контракту.
SETTINGS distributed_foreground_insert = 1,
parallel_distributed_insert_select = 0;