docs(ods): вычитаны комментарии и документация починки раскладки
- Зачем: - правки #137 дважды рассказывали одно и то же: механика капкана жила и в storage.md, и у вставок, а цифры замеров — и в прозе, и в леджере; у читателя это спрашивает больше внимания, чем даёт. - Что: - комментарии у вставок order_parse.sql сжаты до довода с указателем на storage.md; из капкана в storage.md цифры ушли в «Что проверено»; в ingestion.md замеры перенесены из прозы в свой раздел «Что проверено». - Проверка: - рендер order_parse.sql по-прежнему делится ровно на два запроса (из комментария убрана точка с запятой, по которой оператор делит файл). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
94a1ffeee2
commit
45360b6bcf
3 files changed
+36
-41
No files matched your search
@@ -106,24 +106,19 @@ JSON-объектом с точным набором ключей: `order_id`, `
|
|||||||
- `_load_id` — какой запуск Airflow принял строку;
|
- `_load_id` — какой запуск Airflow принял строку;
|
||||||
- `_load_ts` — когда строка приехала в хранилище.
|
- `_load_ts` — когда строка приехала в хранилище.
|
||||||
|
|
||||||
У выжившей после схлопывания строки три последние координаты означают меньше,
|
Версию выбирает только `updated_at`. Заказ, не менявшийся несколько дней,
|
||||||
чем кажется. Версию выбирает только `updated_at`; заказ, не менявшийся
|
приезжает в нескольких слепках одинаковыми деловыми полями, и какая из этих
|
||||||
несколько дней, приезжает в нескольких слепках одинаковыми деловыми полями, и
|
строк переживёт схлопывание — не обещано. Поэтому `snapshot_date` выжившей
|
||||||
какая из этих строк переживёт схлопывание — не обещано. Поэтому её
|
строки — один из дней, когда источник показывал эту версию, не обязательно
|
||||||
`snapshot_date` — *один из* дней, когда источник показывал эту версию, не
|
последний; то же с `_load_id` и `_load_ts`. Вопрос «когда заказ наблюдался в
|
||||||
обязательно последний; то же с `_load_id` и `_load_ts`. Замер 22 августа
|
последний раз» задаётся сырью STG, пока то живо, — а не одной строке ODS.
|
||||||
2026 года (#137): у 86 заказов стартового мира выжил даже не крайний день, а
|
|
||||||
серединный. Вопрос «когда заказ наблюдался в последний раз» задаётся сырью STG,
|
|
||||||
пока его не съел срок жизни, — а не одной строке ODS.
|
|
||||||
|
|
||||||
Вся схема версий стоит на одном свойстве источника: уточнение заказа поднимает
|
Сама схема версий стоит на допущении об источнике: уточнение заказа поднимает
|
||||||
`updated_at`. Наш генератор моделирует источник с триггерной меткой и это
|
`updated_at`. Генератор моделирует источник с триггерной меткой и допущение
|
||||||
свойство гарантирует; замеры 22 августа 2026 года его подтвердили — во всех
|
выполняет (замер — «Что проверено»); источник, умеющий уточнить строку тихо —
|
||||||
парах строк с равным `updated_at` деловые поля совпали побайтово, а по слепкам
|
ручным `UPDATE` мимо приложения, миграцией, — ломает его, и тогда версии по
|
||||||
метка не убывала ни разу. Это допущение, а не закон природы: источник, умеющий
|
метке времени мало: нужна сверка самих полей, в терминах dbt snapshots —
|
||||||
уточнить строку тихо — правка мимо приложения, миграция, ручной `UPDATE`, —
|
стратегия `check` вместо `timestamp`.
|
||||||
эту схему ломает, и тогда версии по метке времени уже не хватает, нужна сверка
|
|
||||||
самих полей (в терминах dbt snapshots — стратегия `check` вместо `timestamp`).
|
|
||||||
|
|
||||||
Обычное чтение `_dist` показывает физически сохранившиеся версии и нужно для
|
Обычное чтение `_dist` показывает физически сохранившиеся версии и нужно для
|
||||||
диагностики. Их число зависит от фоновых слияний: ODS не служит архивом истории.
|
диагностики. Их число зависит от фоновых слияний: ODS не служит архивом истории.
|
||||||
@@ -205,6 +200,13 @@ JSON-объектом с точным набором ключей: `order_id`, `
|
|||||||
`system.query_log` на первой ноде осталось ровно по одному завершённому запросу
|
`system.query_log` на первой ноде осталось ровно по одному завершённому запросу
|
||||||
пользователя `etl` для забора в STG, записи годных строк и записи брака.
|
пользователя `etl` для забора в STG, записи годных строк и записи брака.
|
||||||
|
|
||||||
|
Допущение и произвол ярлыков замерены 22 августа 2026 года при исполнении
|
||||||
|
#137: во всех 3034 парах строк с равным `updated_at` деловые поля совпали
|
||||||
|
побайтово, по слепкам метка ни разу не убывала, а `ods.order_v` сошёлся
|
||||||
|
построчно с эталоном `argMax` по `updated_at`, посчитанным прямо из сырья.
|
||||||
|
Произвол ярлыков виден въявь: у 86 заказов стартового мира выжила строка
|
||||||
|
серединного слепка — не первого и не последнего.
|
||||||
|
|
||||||
Забор из Kafka в STG снят на живом стенде 18 августа 2026 года при исполнении
|
Забор из Kafka в STG снят на живом стенде 18 августа 2026 года при исполнении
|
||||||
#93: одно прямое чтение приносит весь слепок дня, метаданные доставки доступны,
|
#93: одно прямое чтение приносит весь слепок дня, метаданные доставки доступны,
|
||||||
офсеты коммитятся, а сбой забора не двигает позицию мира. Числа — [ADR
|
офсеты коммитятся, а сбой забора не двигает позицию мира. Числа — [ADR
|
||||||
|
|||||||
@@ -87,15 +87,14 @@ CSV-файла. Живёт он вне цепочки слоёв, в зоне `d
|
|||||||
Разные ключи у слоёв ставят капкан на вставке `INSERT INTO цель_dist SELECT …
|
Разные ключи у слоёв ставят капкан на вставке `INSERT INTO цель_dist SELECT …
|
||||||
FROM источник_dist`. Заводское значение `parallel_distributed_insert_select` —
|
FROM источник_dist`. Заводское значение `parallel_distributed_insert_select` —
|
||||||
2: если обе таблицы на одном кластере, вставка исполняется локально на каждом
|
2: если обе таблицы на одном кластере, вставка исполняется локально на каждом
|
||||||
шарде, и шард пишет прямо в локальную таблицу цели, минуя её ключ шардирования.
|
шарде, минуя ключ шардирования цели. Строки наследуют раскладку источника, и
|
||||||
Строки наследуют раскладку источника, и ошибки при этом нет: расхождение видно
|
ошибки при этом нет — расхождение видно только пересчётом ключей по
|
||||||
только пересчётом по `hostName()` — сколько ключей встретилось больше чем на
|
`hostName()`. Так половина заказов и легла на оба шарда, где
|
||||||
одном шарде. На разборе заказов так и вышло: 1189 из 1662 заказов легли
|
`ReplacingMergeTree` через границу шарда дубли не схлопывает (замер — «Что
|
||||||
одинаковыми строками на оба шарда, а `ReplacingMergeTree` через границу шарда их
|
проверено», #137). Лекарство — явное `parallel_distributed_insert_select = 0`
|
||||||
не схлопывает. Поэтому у такой вставки в `SETTINGS` стоит явное
|
в `SETTINGS` такой вставки: раскладку делает инициатор, по ключу цели
|
||||||
`parallel_distributed_insert_select = 0`: раскладку делает инициатор, по ключу
|
(`sql/ods/order_parse.sql`). Капкан ждёт и сборки DDS: они тоже будут читать
|
||||||
цели (`sql/ods/order_parse.sql`). Правило нужно и дальше — сборки DDS тоже будут
|
`_dist` и писать в `_dist` с другим ключом.
|
||||||
читать `_dist` и писать в `_dist` с другим ключом.
|
|
||||||
|
|
||||||
Ключи ко-локации названы заранее, потому что на них стоит политика соединений из
|
Ключи ко-локации названы заранее, потому что на них стоит политика соединений из
|
||||||
раздела 6 спеки: обычное соединение разрешено только по ключу ко-локации, всё
|
раздела 6 спеки: обычное соединение разрешено только по ключу ко-локации, всё
|
||||||
@@ -592,9 +591,8 @@ DDL-словарь с файловым источником внутри `user_f
|
|||||||
на каждом шарде, `2` — `SELECT` и `INSERT` на каждом шарде; заводское — `2`.
|
на каждом шарде, `2` — `SELECT` и `INSERT` на каждом шарде; заводское — `2`.
|
||||||
На живых данных видны и дефект, и починка: до правки 1189 из 1662 заказов
|
На живых данных видны и дефект, и починка: до правки 1189 из 1662 заказов
|
||||||
лежали на обоих шардах, после явного нуля и повторного разбора того же
|
лежали на обоих шардах, после явного нуля и повторного разбора того же
|
||||||
`_load_id` — ни одного, каждый шард держит только свои ключи. Вершина версий
|
`_load_id` — ни одного. Вершина версий уцелела: деловые поля `ods.order_v`
|
||||||
уцелела: деловые поля `ods.order_v` сошлись с эталоном, посчитанным прямо из
|
сошлись с эталоном, посчитанным прямо из сырья.
|
||||||
сырья.
|
|
||||||
|
|
||||||
**Проверка версий заказов 18 августа 2026 года (#94).** MCP Context7 подтвердил,
|
**Проверка версий заказов 18 августа 2026 года (#94).** MCP Context7 подтвердил,
|
||||||
что `JSONType` возвращает имя типа значения JSON, — на нём стоит проверка типов
|
что `JSONType` возвращает имя типа значения JSON, — на нём стоит проверка типов
|
||||||
|
|||||||
+7
-12
@@ -92,14 +92,11 @@ SELECT
|
|||||||
_load_ts
|
_load_ts
|
||||||
FROM stg.orders_raw_dist
|
FROM stg.orders_raw_dist
|
||||||
WHERE _load_id = {load_id:String} AND row_is_valid
|
WHERE _load_id = {load_id:String} AND row_is_valid
|
||||||
-- Заводское значение parallel_distributed_insert_select — 2: вставку вида
|
-- Заводская двойка parallel_distributed_insert_select исполняет вставку из
|
||||||
-- INSERT ... SELECT из Distributed в Distributed на одном кластере ClickHouse
|
-- Distributed в Distributed локально на каждом шарде, минуя ключ шардирования
|
||||||
-- исполняет локально на каждом шарде, и строки минуют ключ шардирования цели.
|
-- цели, — так заказы и расползлись по обоим шардам. Ноль отдаёт раскладку
|
||||||
-- Ключи здесь разные — сырьё разложено по хешу строки, заказы по
|
-- инициатору, по ключу цели. Капкан целиком — docs/architecture/storage.md,
|
||||||
-- cityHash64(order_id), — так версии одного заказа и расползлись по обоим
|
-- «Раскладка по шардам».
|
||||||
-- шардам, где ReplacingMergeTree их уже не схлопывает. Ноль возвращает вставку
|
|
||||||
-- инициатору: он читает срез со всех шардов и пишет через ods.order_dist,
|
|
||||||
-- то есть по её ключу.
|
|
||||||
SETTINGS distributed_foreground_insert = 1,
|
SETTINGS distributed_foreground_insert = 1,
|
||||||
parallel_distributed_insert_select = 0;
|
parallel_distributed_insert_select = 0;
|
||||||
|
|
||||||
@@ -127,9 +124,7 @@ SELECT
|
|||||||
_load_ts
|
_load_ts
|
||||||
FROM stg.orders_raw_dist
|
FROM stg.orders_raw_dist
|
||||||
WHERE _load_id = {load_id:String} AND NOT row_is_valid
|
WHERE _load_id = {load_id:String} AND NOT row_is_valid
|
||||||
-- Тот же ноль, хотя здесь заводское значение раскладку не ломало: ключ у
|
-- Тот же ноль: ключ брака сегодня совпадает с ключом сырья, и локальная
|
||||||
-- ods.order_errors_dist — cityHash64(raw), тот же, что у сырья, и локальная
|
-- запись легла бы верно — но по совпадению, а не по контракту.
|
||||||
-- запись попадала бы в свой шард сама. Совпадение ключей не гарантия:
|
|
||||||
-- поменяется ключ любой из двух таблиц — и строки разъедутся молча.
|
|
||||||
SETTINGS distributed_foreground_insert = 1,
|
SETTINGS distributed_foreground_insert = 1,
|
||||||
parallel_distributed_insert_select = 0;
|
parallel_distributed_insert_select = 0;
|
||||||
Reference in new issue
Block a user