Версионный ODS заказов: строгий переход, брак и ods.order_v #94

Closed
opened 2026-08-17 22:54:57 +03:00 by ddmitry · 0 comments
Owner

Part of #5.

Цель

Типизированные версии заказов: один следующий task_id дага приёма, два
последовательных INSERT SELECT по неизменному срезу _load_id — годные
строки в ods.order_snapshot, брак в _errors; ods.order_v
единственный корректный способ прочитать текущее состояние. Учебный
результат (ingestion.md): менти различает версию бизнес-сущности,
наблюдение источника и запуск загрузки.

Что войдёт

  • ods.order_snapshot_rep / _dist: ReplacingMergeTree(updated_at),
    ORDER BY order_id, партиция по дню неизменного created_at,
    шардирование cityHash64(order_id) — только так версии одного заказа
    оказываются на одном шарде и FINAL корректен через распределённую
    таблицу.
  • ods.order_snapshot_errors_rep / _dist: сырой текст, метаданные
    доставки, _load_id, класс брака. Сроки хранения и типы — по конвенциям
    storage.md.
  • Переход — один следующий task_id дага приёма после забора: общий
    предикат брака и его буквальное отрицание; функции предиката не бросают
    исключений, предикат всегда заканчивается в true/false, не в NULL.
    Транзакции между целями нет: при частичном сбое повторяется весь
    task_id, служебные метки не вычисляются заново.
  • Граница строгости — форма провода: корень — JSON-объект с точным набором
    11 ключей; скаляры по типу JSON; деньги — строки с ровно двумя знаками;
    времена — RFC 3339 в UTC с миллисекундами; snapshot_date
    YYYY-MM-DD; items — только JSON-массив, внутрь не смотреть.
  • Классы брака первым совпадением: not_an_objectkeyset_mismatch
    field_invalid; имя поля в класс не входит.
  • ods.order_v: сначала простое представление над _dist FINAL; способ
    выбора можно заменить, не меняя потребителей.
  • Результаты опытов — в «Риски и проверка» ingestion.md тем же PR;
    storage.md — при расхождении.

Границы

ingestion.md, «Не входит», целиком: поля внутри items, переходы статуса
и равенства сумм не проверять; модель DDS не строить (_load_id выше ODS —
#85, этап 4); удаление заказа по отсутствию в слепке не делать; маркер конца
слепка и транзакция между целями не заводятся.

Кому что

Строгий предикат и DDL — Opus: учебный SQL, на котором стоит урок тикета. Кодексу — опыты: малая порция брака, повторы task_id, уборка партиции-донора. Бриф Кодексу обязан требовать: проверять свойство, а не текст вывода грепом; только POSIX-инструменты (ripgrep на машине стенда нет); вердикт «недостижимо» перепроверять на достижимость, а не на факт.

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

  • docs/architecture/orders/ingestion.md — целиком, это спека тикета;
  • docs/adr/0010-order-versions-in-ods.md;
  • docs/research/2026-08-16-order-snapshot-wire-format.md;
  • docs/architecture/storage.md — конвенции и сроки;
  • docs/architecture/testing.md — «Интеграционная проверка постоянной
    целью не становится»: за опытами прибираются;
  • опыт #43: порченые сообщения — мутацией настоящего слепка из файлового
    приёмника; при показе красного через правку Python —
    PYTHONDONTWRITEBYTECODE=1; ожидание — опросом с таймаутом; инструменты
    POSIX.

Проверка

Опыты — разовая приёмка, не постоянные сторожа; результаты — в тело PR.
Опытные строки кладутся в партицию за границей оси мира (день вне эталона)
и сносятся после опыта — данные, которых мир не рождал, не должны лечь в
лабы менти. Расхождение опыта с ожиданием — вопрос владельцу, а не починка
на месте.

  • make lint, make config-test; на стенде make smoke,
    make check-clickhouse зелёные.
  • Честный прогон дня: _errors пуст, сумма «годные плюс брак» равна
    числу отправленных.
  • Малая управляемая порция: по одной строке каждого класса брака и две
    годные версии одного order_id — обе цели сохранили все непустые
    сообщения, ods.order_v вернула новую версию независимо от фонового
    слияния.
  • Повтор task_id с тем же _load_id: строка в ods.order_v и её
    _load_ts не изменились; версии одного заказа — на одном шарде и в
    одной партиции. Таблица ошибок может записать тот же брак повторно:
    совпавшие _load_id и Kafka-координаты показывают повтор задачи.
  • После опытов партиция-донор снесена; следы опытов в ODS и _errors
    не остались.
Part of #5. ## Цель Типизированные версии заказов: один следующий `task_id` дага приёма, два последовательных `INSERT SELECT` по неизменному срезу `_load_id` — годные строки в `ods.order_snapshot`, брак в `_errors`; `ods.order_v` — единственный корректный способ прочитать текущее состояние. Учебный результат (`ingestion.md`): менти различает версию бизнес-сущности, наблюдение источника и запуск загрузки. ## Что войдёт - `ods.order_snapshot_rep` / `_dist`: `ReplacingMergeTree(updated_at)`, `ORDER BY order_id`, партиция по дню неизменного `created_at`, шардирование `cityHash64(order_id)` — только так версии одного заказа оказываются на одном шарде и `FINAL` корректен через распределённую таблицу. - `ods.order_snapshot_errors_rep` / `_dist`: сырой текст, метаданные доставки, `_load_id`, класс брака. Сроки хранения и типы — по конвенциям `storage.md`. - Переход — один следующий `task_id` дага приёма после забора: общий предикат брака и его буквальное отрицание; функции предиката не бросают исключений, предикат всегда заканчивается в `true`/`false`, не в `NULL`. Транзакции между целями нет: при частичном сбое повторяется весь `task_id`, служебные метки не вычисляются заново. - Граница строгости — форма провода: корень — JSON-объект с точным набором 11 ключей; скаляры по типу JSON; деньги — строки с ровно двумя знаками; времена — RFC 3339 в UTC с миллисекундами; `snapshot_date` — `YYYY-MM-DD`; `items` — только JSON-массив, внутрь не смотреть. - Классы брака первым совпадением: `not_an_object` → `keyset_mismatch` → `field_invalid`; имя поля в класс не входит. - `ods.order_v`: сначала простое представление над `_dist FINAL`; способ выбора можно заменить, не меняя потребителей. - Результаты опытов — в «Риски и проверка» `ingestion.md` тем же PR; `storage.md` — при расхождении. ## Границы `ingestion.md`, «Не входит», целиком: поля внутри `items`, переходы статуса и равенства сумм не проверять; модель DDS не строить (`_load_id` выше ODS — #85, этап 4); удаление заказа по отсутствию в слепке не делать; маркер конца слепка и транзакция между целями не заводятся. ## Кому что Строгий предикат и DDL — Opus: учебный SQL, на котором стоит урок тикета. Кодексу — опыты: малая порция брака, повторы `task_id`, уборка партиции-донора. Бриф Кодексу обязан требовать: проверять свойство, а не текст вывода грепом; только POSIX-инструменты (ripgrep на машине стенда нет); вердикт «недостижимо» перепроверять на достижимость, а не на факт. ## Сначала прочитать - `docs/architecture/orders/ingestion.md` — целиком, это спека тикета; - `docs/adr/0010-order-versions-in-ods.md`; - `docs/research/2026-08-16-order-snapshot-wire-format.md`; - `docs/architecture/storage.md` — конвенции и сроки; - `docs/architecture/testing.md` — «Интеграционная проверка постоянной целью не становится»: за опытами прибираются; - опыт #43: порченые сообщения — мутацией настоящего слепка из файлового приёмника; при показе красного через правку Python — `PYTHONDONTWRITEBYTECODE=1`; ожидание — опросом с таймаутом; инструменты POSIX. ## Проверка Опыты — разовая приёмка, не постоянные сторожа; результаты — в тело PR. Опытные строки кладутся в партицию за границей оси мира (день вне эталона) и сносятся после опыта — данные, которых мир не рождал, не должны лечь в лабы менти. Расхождение опыта с ожиданием — вопрос владельцу, а не починка на месте. - [ ] `make lint`, `make config-test`; на стенде `make smoke`, `make check-clickhouse` зелёные. - [ ] Честный прогон дня: `_errors` пуст, сумма «годные плюс брак» равна числу отправленных. - [ ] Малая управляемая порция: по одной строке каждого класса брака и две годные версии одного `order_id` — обе цели сохранили все непустые сообщения, `ods.order_v` вернула новую версию независимо от фонового слияния. - [ ] Повтор `task_id` с тем же `_load_id`: строка в `ods.order_v` и её `_load_ts` не изменились; версии одного заказа — на одном шарде и в одной партиции. Таблица ошибок может записать тот же брак повторно: совпавшие `_load_id` и Kafka-координаты показывают повтор задачи. - [ ] После опытов партиция-донор снесена; следы опытов в ODS и `_errors` не остались.
ddmitry added the ready-for-agent label 2026-08-17 22:56:19 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#94