Решить судьбу координат доставки в ods.order — snapshot_date, _load_id, _load_ts. Сегодня они обещают больше, чем слой может держать: ods.order_rep
схлопывает доставки движком ReplacingMergeTree(updated_at), а у заказа,
который не менялся, все доставки равны по версии. Какая строка переживёт
слияние — произвол, и ярлык выжившей не обязан быть последним. Приём это
признаёт прямым текстом (ingestion.md, «Роль ODS»), то есть слой хранит поле
и тут же просит ему не верить.
Тот же довод репозиторий уже применил этажом выше: в dds/order.md ярлыки ODS
не переезжают в модель именно потому, что «перенесённый ярлык выглядел бы
фактом, оставаясь жребием; лживая координата хуже отсутствующей». Этот тикет —
о том, распространяется ли правило на сам ODS.
Откуда взялось
Долг проявился при исполнении #151 (модель заказа в DDS). Трансформации нужен
был ответ на вопрос «какие дни принёс последний слепок» — и оказалось, что ODS
на него ответить не может. Плата вышла такая:
окно пересборки считается не по слепку, а по позднейшему дню заказа, и
требует константы K, которая живёт двумя копиями — в generator/world.py
и в compose.yaml, без всякой сверки между ними;
край окна съезжает на день назад, если в позднейший день источника не
родилось ни одного заказа;
ревью #151 отдельно отметило дрейф K как MAJOR-риск.
Замер на стартовом мире 26 августа 2026 года: последний слепок нёс все 1662
заказа, а его ярлык сохранили 1188 строк — четверть заказов отбор по snapshot_date потерял бы молча.
Если бы ярлык был правдивым, окно бралось бы прямо из данных, а константа K
на стороне стенда исчезла бы вместе с риском дрейфа.
Что решить
Три исхода, все допустимы:
Убрать координаты доставки из ODS. Вопрос «что приехало» задаётся тому,
кто может ответить: сырью STG, пока живо, или журналу загрузок, если он
появится. Лживого поля просто нет. Цена — расследование происшествия теряет
шаг, а журнал загрузок это отдельная постройка.
Сделать ярлык правдивым. Доломать ничью по updated_at датой слепка,
чтобы выживала строка последней доставки. Деловой ответ не меняется — у
равных версий поля совпадают, — а ярлык начинает значить «последний раз
виден тогда-то». Цена — корректность привязывается к монотонности слепков:
доставка не по порядку выберет версию по ярлыку, а не по делу.
Оставить как есть, записав довод. Тогда K остаётся копией, а сверку
копий берёт на себя контур DQ.
Развилка существенная, поэтому вести её через brainstorm-with-docs: сначала
веер, потом конвергенция, отклонённые варианты — в запись.
Границы
Тикет вправе ограничиться этой логикой, но чинить её сквозным вариантом:
DDL ODS, шаг разбора, дока приёма, ADR и потребители — всё одним PR, чтобы
контракт нигде не разошёлся сам с собой.
Вправе и отклонить: обоснованный отказ с записью — законный исход.
Окно пересборки в DDS этим тикетом не переписывается. Если решение упростит sql/dds/order_scope.sql и снимет K со стенда, это делается тем же PR — но
как следствие, а не как самостоятельная цель.
Журнал загрузок как отдельная сущность — не здесь.
Сначала прочитать
docs/architecture/orders/ingestion.md — «Роль ODS», четыре координаты
строки и признание про жребий;
docs/adr/0010-order-versions-in-ods.md — почему версия выбирается по updated_at;
docs/architecture/dds/order.md — «Служебные колонки», правило про лживую
координату;
docs/architecture/etl.md — «Объём прогона» и «Чего конвейер о себе не
знает»;
sql/dds/order_scope.sql — что трансформация делает вместо честного ответа.
Критерии приёмки
Решение записано там, где живёт контракт: ADR 0010 либо orders/ingestion.md, и оба текста не противоречат друг другу.
Если координаты меняются — DDL, разбор, дока и потребители правятся
одним PR; мир пересобирается, приём заказов остаётся зелёным.
Если ярлык становится правдивым — показано замером, что после нескольких
слепков snapshot_date выжившей строки равен последнему слепку, который
её нёс.
Если решено оставить — довод записан рядом с полем, и сверка копий K
названа задачей контура DQ.
make lint; на стенде make smoke, make check-clickhouse, make check-services зелёные.
## Цель
Решить судьбу координат доставки в `ods.order` — `snapshot_date`, `_load_id`,
`_load_ts`. Сегодня они обещают больше, чем слой может держать: `ods.order_rep`
схлопывает доставки движком `ReplacingMergeTree(updated_at)`, а у заказа,
который не менялся, все доставки равны по версии. Какая строка переживёт
слияние — произвол, и ярлык выжившей не обязан быть последним. Приём это
признаёт прямым текстом (`ingestion.md`, «Роль ODS»), то есть слой хранит поле
и тут же просит ему не верить.
Тот же довод репозиторий уже применил этажом выше: в `dds/order.md` ярлыки ODS
не переезжают в модель именно потому, что «перенесённый ярлык выглядел бы
фактом, оставаясь жребием; лживая координата хуже отсутствующей». Этот тикет —
о том, распространяется ли правило на сам ODS.
## Откуда взялось
Долг проявился при исполнении #151 (модель заказа в DDS). Трансформации нужен
был ответ на вопрос «какие дни принёс последний слепок» — и оказалось, что ODS
на него ответить не может. Плата вышла такая:
- окно пересборки считается не по слепку, а по позднейшему дню заказа, и
требует константы K, которая живёт **двумя копиями** — в `generator/world.py`
и в `compose.yaml`, без всякой сверки между ними;
- край окна съезжает на день назад, если в позднейший день источника не
родилось ни одного заказа;
- ревью #151 отдельно отметило дрейф K как MAJOR-риск.
Замер на стартовом мире 26 августа 2026 года: последний слепок нёс все 1662
заказа, а его ярлык сохранили 1188 строк — четверть заказов отбор по
`snapshot_date` потерял бы молча.
**Если бы ярлык был правдивым, окно бралось бы прямо из данных, а константа K
на стороне стенда исчезла бы вместе с риском дрейфа.**
## Что решить
Три исхода, все допустимы:
1. **Убрать координаты доставки из ODS.** Вопрос «что приехало» задаётся тому,
кто может ответить: сырью STG, пока живо, или журналу загрузок, если он
появится. Лживого поля просто нет. Цена — расследование происшествия теряет
шаг, а журнал загрузок это отдельная постройка.
2. **Сделать ярлык правдивым.** Доломать ничью по `updated_at` датой слепка,
чтобы выживала строка последней доставки. Деловой ответ не меняется — у
равных версий поля совпадают, — а ярлык начинает значить «последний раз
виден тогда-то». Цена — корректность привязывается к монотонности слепков:
доставка не по порядку выберет версию по ярлыку, а не по делу.
3. **Оставить как есть**, записав довод. Тогда K остаётся копией, а сверку
копий берёт на себя контур DQ.
Развилка существенная, поэтому вести её через `brainstorm-with-docs`: сначала
веер, потом конвергенция, отклонённые варианты — в запись.
## Границы
- Тикет вправе ограничиться **этой** логикой, но чинить её сквозным вариантом:
DDL ODS, шаг разбора, дока приёма, ADR и потребители — всё одним PR, чтобы
контракт нигде не разошёлся сам с собой.
- Вправе и отклонить: обоснованный отказ с записью — законный исход.
- Окно пересборки в DDS этим тикетом не переписывается. Если решение упростит
`sql/dds/order_scope.sql` и снимет K со стенда, это делается тем же PR — но
как следствие, а не как самостоятельная цель.
- Журнал загрузок как отдельная сущность — не здесь.
## Сначала прочитать
- `docs/architecture/orders/ingestion.md` — «Роль ODS», четыре координаты
строки и признание про жребий;
- `docs/adr/0010-order-versions-in-ods.md` — почему версия выбирается по
`updated_at`;
- `docs/architecture/dds/order.md` — «Служебные колонки», правило про лживую
координату;
- `docs/architecture/etl.md` — «Объём прогона» и «Чего конвейер о себе не
знает»;
- `sql/dds/order_scope.sql` — что трансформация делает вместо честного ответа.
## Критерии приёмки
- [x] Решение записано там, где живёт контракт: ADR 0010 либо
`orders/ingestion.md`, и оба текста не противоречат друг другу.
- [ ] Если координаты меняются — DDL, разбор, дока и потребители правятся
одним PR; мир пересобирается, приём заказов остаётся зелёным.
- [ ] Если ярлык становится правдивым — показано замером, что после нескольких
слепков `snapshot_date` выжившей строки равен последнему слепку, который
её нёс.
- [ ] Если решено оставить — довод записан рядом с полем, и сверка копий K
названа задачей контура DQ.
- [ ] `make lint`; на стенде `make smoke`, `make check-clickhouse`,
`make check-services` зелёные.
Резолюция развилки. Выбран второй исход, но в форме, которой веер постановки не содержал: ярлык становится правдивым не добивкой ничьей, а сменой самой колонки версии — ReplacingMergeTree(snapshot_date). Формула решения: у полного слепка версия — сам слепок; истина — то, что источник сказал последним.
Решение записано в ADR 0014 (коммит c361c36) — там ответ доводу ADR 0010, границы допущения и веер отклонённых: обе склейки версий, дедупликация на вставке, удаление поля, статус-кво. ADR 0010 помечен пересмотренным в части колонки версии.
Перед записью решение прошло два холодных ревью свежими сессиями без контекста обсуждения.
Развёртка зависимостей: полный список мест, где updated_at назван версией, — перенесён в объём #165. Попутно выяснилось, что «одна дата — один слепок» и монотонность метки — не допущения, а тестированные инварианты генератора.
Адверсарное: вердикт «выстояло», сломать деловое содержимое внутри модели не удалось. Главная находка: смена движка не подхватывается живым миром (DDL исполняется при рождении, CREATE IF NOT EXISTS) — решение вступает в силу только пересозданием хранилища. Владелец решил обойтись без сторожа в скриптах: мир пересобирается один раз.
Замер 1662/1188 из постановки объяснился до конца: документация ClickHouse обещает при ничьей победу последней вставленной строки, а стенд показал обратное (86 заказов с ярлыком серединного слепка) — на слово докам здесь верить нельзя, и это зафиксировано в ADR.
Реализация — #165: DDL с пересборкой мира, снятие K со стенда, упрощение order_scope.sql, правка документов одним PR. Первый критерий приёмки закрыт ADR; остальные — условные ветки исполнения и переезжают в #165 вместе с замером правдивости ярлыка.
Резолюция развилки. Выбран второй исход, но в форме, которой веер постановки не содержал: ярлык становится правдивым не добивкой ничьей, а сменой самой колонки версии — `ReplacingMergeTree(snapshot_date)`. Формула решения: у полного слепка версия — сам слепок; истина — то, что источник сказал последним.
Решение записано в [ADR 0014](https://git.dementev.space/ddmitry/clickstream-data-platform/src/branch/main/docs/adr/0014-order-version-snapshot-date.md) (коммит c361c36) — там ответ доводу ADR 0010, границы допущения и веер отклонённых: обе склейки версий, дедупликация на вставке, удаление поля, статус-кво. ADR 0010 помечен пересмотренным в части колонки версии.
Перед записью решение прошло два холодных ревью свежими сессиями без контекста обсуждения.
- Развёртка зависимостей: полный список мест, где `updated_at` назван версией, — перенесён в объём #165. Попутно выяснилось, что «одна дата — один слепок» и монотонность метки — не допущения, а тестированные инварианты генератора.
- Адверсарное: вердикт «выстояло», сломать деловое содержимое внутри модели не удалось. Главная находка: смена движка не подхватывается живым миром (DDL исполняется при рождении, `CREATE IF NOT EXISTS`) — решение вступает в силу только пересозданием хранилища. Владелец решил обойтись без сторожа в скриптах: мир пересобирается один раз.
Замер 1662/1188 из постановки объяснился до конца: документация ClickHouse обещает при ничьей победу последней вставленной строки, а стенд показал обратное (86 заказов с ярлыком серединного слепка) — на слово докам здесь верить нельзя, и это зафиксировано в ADR.
Реализация — #165: DDL с пересборкой мира, снятие K со стенда, упрощение `order_scope.sql`, правка документов одним PR. Первый критерий приёмки закрыт ADR; остальные — условные ветки исполнения и переезжают в #165 вместе с замером правдивости ярлыка.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Цель
Решить судьбу координат доставки в
ods.order—snapshot_date,_load_id,_load_ts. Сегодня они обещают больше, чем слой может держать:ods.order_repсхлопывает доставки движком
ReplacingMergeTree(updated_at), а у заказа,который не менялся, все доставки равны по версии. Какая строка переживёт
слияние — произвол, и ярлык выжившей не обязан быть последним. Приём это
признаёт прямым текстом (
ingestion.md, «Роль ODS»), то есть слой хранит полеи тут же просит ему не верить.
Тот же довод репозиторий уже применил этажом выше: в
dds/order.mdярлыки ODSне переезжают в модель именно потому, что «перенесённый ярлык выглядел бы
фактом, оставаясь жребием; лживая координата хуже отсутствующей». Этот тикет —
о том, распространяется ли правило на сам ODS.
Откуда взялось
Долг проявился при исполнении #151 (модель заказа в DDS). Трансформации нужен
был ответ на вопрос «какие дни принёс последний слепок» — и оказалось, что ODS
на него ответить не может. Плата вышла такая:
требует константы K, которая живёт двумя копиями — в
generator/world.pyи в
compose.yaml, без всякой сверки между ними;родилось ни одного заказа;
Замер на стартовом мире 26 августа 2026 года: последний слепок нёс все 1662
заказа, а его ярлык сохранили 1188 строк — четверть заказов отбор по
snapshot_dateпотерял бы молча.Если бы ярлык был правдивым, окно бралось бы прямо из данных, а константа K
на стороне стенда исчезла бы вместе с риском дрейфа.
Что решить
Три исхода, все допустимы:
кто может ответить: сырью STG, пока живо, или журналу загрузок, если он
появится. Лживого поля просто нет. Цена — расследование происшествия теряет
шаг, а журнал загрузок это отдельная постройка.
updated_atдатой слепка,чтобы выживала строка последней доставки. Деловой ответ не меняется — у
равных версий поля совпадают, — а ярлык начинает значить «последний раз
виден тогда-то». Цена — корректность привязывается к монотонности слепков:
доставка не по порядку выберет версию по ярлыку, а не по делу.
копий берёт на себя контур DQ.
Развилка существенная, поэтому вести её через
brainstorm-with-docs: сначалавеер, потом конвергенция, отклонённые варианты — в запись.
Границы
DDL ODS, шаг разбора, дока приёма, ADR и потребители — всё одним PR, чтобы
контракт нигде не разошёлся сам с собой.
sql/dds/order_scope.sqlи снимет K со стенда, это делается тем же PR — нокак следствие, а не как самостоятельная цель.
Сначала прочитать
docs/architecture/orders/ingestion.md— «Роль ODS», четыре координатыстроки и признание про жребий;
docs/adr/0010-order-versions-in-ods.md— почему версия выбирается поupdated_at;docs/architecture/dds/order.md— «Служебные колонки», правило про лживуюкоординату;
docs/architecture/etl.md— «Объём прогона» и «Чего конвейер о себе незнает»;
sql/dds/order_scope.sql— что трансформация делает вместо честного ответа.Критерии приёмки
orders/ingestion.md, и оба текста не противоречат друг другу.одним PR; мир пересобирается, приём заказов остаётся зелёным.
слепков
snapshot_dateвыжившей строки равен последнему слепку, которыйеё нёс.
названа задачей контура DQ.
make lint; на стендеmake smoke,make check-clickhouse,make check-servicesзелёные.Родился при исполнении #151: там же лежат замеры (1662 против 1188) и вывод объёма, который обходится без правдивого ярлыка.
Резолюция развилки. Выбран второй исход, но в форме, которой веер постановки не содержал: ярлык становится правдивым не добивкой ничьей, а сменой самой колонки версии —
ReplacingMergeTree(snapshot_date). Формула решения: у полного слепка версия — сам слепок; истина — то, что источник сказал последним.Решение записано в ADR 0014 (коммит
c361c36) — там ответ доводу ADR 0010, границы допущения и веер отклонённых: обе склейки версий, дедупликация на вставке, удаление поля, статус-кво. ADR 0010 помечен пересмотренным в части колонки версии.Перед записью решение прошло два холодных ревью свежими сессиями без контекста обсуждения.
updated_atназван версией, — перенесён в объём #165. Попутно выяснилось, что «одна дата — один слепок» и монотонность метки — не допущения, а тестированные инварианты генератора.CREATE IF NOT EXISTS) — решение вступает в силу только пересозданием хранилища. Владелец решил обойтись без сторожа в скриптах: мир пересобирается один раз.Замер 1662/1188 из постановки объяснился до конца: документация ClickHouse обещает при ничьей победу последней вставленной строки, а стенд показал обратное (86 заказов с ярлыком серединного слепка) — на слово докам здесь верить нельзя, и это зафиксировано в ADR.
Реализация — #165: DDL с пересборкой мира, снятие K со стенда, упрощение
order_scope.sql, правка документов одним PR. Первый критерий приёмки закрыт ADR; остальные — условные ветки исполнения и переезжают в #165 вместе с замером правдивости ярлыка.