- Зачем:
- лотерея ярлыков доставки при ничьей по updated_at делала слой
недетерминированным и держала окно K двумя копиями без сверки (#163).
- Что:
- добавлен ADR 0014: версия ods.order — snapshot_date, с ответом доводу
ADR 0010, границами допущения и веером отклонённых вариантов.
- в ADR 0010 помечен пересмотр колонки версии.
- Проверка:
- реализация и замеры — тикет #165; решение кодом пока не применено.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.3 KiB
ADR 0014. Версия заказа в ODS: дата слепка
Дата: 26 августа 2026 года. Статус: принято. Пересматривает колонку версии из ADR 0010; остальные решения того ADR в силе. Развилка разобрана в #163, реализация — #165.
Решение
ods.order хранит версии заказа в ReplacingMergeTree(snapshot_date): при
схлопывании выживает строка позднейшего слепка, который нёс заказ. Формула
решения: у полного слепка версия — сам слепок; истина — то, что источник
сказал последним.
Ключ сущности, партиция и шардирование не меняются. updated_at остаётся
деловым полем, и деловой ответ слоя от смены версии не зависит: на
канонических слепках выбор по дате слепка побайтово совпадает с выбором по
деловой метке, потому что метка монотонна по слепкам, а слепок дня — чистая
функция зерна и дня (тесты генератора
test_the_state_is_read_at_the_boundary_of_the_day и
test_two_runs_give_the_same_snapshot).
Решение покупает две вещи.
- Детерминизм слоя. Разные слепки несут разные даты — ничьей между
содержательно разными строками не бывает. При версии
updated_atдоставки неизменившегося заказа равны по версии, и выжившая строка — жребий: у 86 заказов стартового мира выживала строка серединного слепка, притом что у всех 3034 пар строк с равнымupdated_atделовые поля совпадали побайтово (замеры #137). Документация ClickHouse обещает при ничьей победу последней вставленной строки — замер это опроверг, полагаться на неё было бы ошибкой. - Правдивый ярлык.
snapshot_dateвыжившей строки — последний слепок, который нёс заказ. Объём пересборки DDS читается прямо из данных, причём безFINAL: слияние сохраняет максимум даты по построению. Константа окна K уходит со стороны стенда вместе с риском дрейфа её двух копий (ревью #151 ставило этому риску MAJOR).
Ответ доводу ADR 0010
ADR 0010 отверг семейство «публикацию решает дата наблюдения»: у потребителя нет признака полноты слепка. Довод целил в подмену партиции — ей полнота нужна, потому что неполная партиция стёрла бы уже принятые версии. Колонке версии полнота не нужна: она сравнивает свежесть строк одного заказа, и недоехавшая строка просто оставляет в силе предыдущую — ровно как при версии по деловой метке.
Границы допущения
- Дисциплина слепков. Слепок с поздней датой не несёт состояния старше. Источник, собирающий слепок из отставшей реплики, это ломает; модельный мир — нет: см. тесты выше.
- Одна дата — один слепок. Ограничение вшито в движок: вторая внутридневная выгрузка дала бы ничью уже на деловых полях. Это условие контракта источника, его место — в границе строгого приёма (приём заказов).
- Окно наследует потерю строк. Частичный отказ вставки после коммита офсетов может унести старший день последнего слепка — выведенное окно молча сузится. Лекарство прежнее, сверка слоя с источником в контуре DQ (конвейер, «Чего конвейер о себе не знает»).
- Тихая правка лечится только внутри окна. Строку, изменённую без
сдвига
updated_at, перекроет следующий слепок; замёрзший заказ источник больше не показывает, там лечит лишь сверка полей. - Повторная доставка слепка. Байты совпадают, жребий остаётся только на
_load_idи_load_ts; ярлык даты детерминирован. - Правка живого мира не подхватится. DDL исполняется при рождении мира,
ALTERколонку версии не меняет: решение вступает в силу пересозданием хранилища (make rebuild-storage).
Отклонённые варианты
- Оставить как есть, сверку копий K отдать DQ. Лотерея ярлыков остаётся задокументированным грехом, окно — константой с риском дрейфа.
- Убрать координаты доставки из ODS. Наказывает поле за вопрос, на который оно не бралось отвечать: расследованию (связка годных строк, ошибок и сырья) координаты служили честно.
- Дедупликация на вставке. Детерминизм «обычно», не по построению: анти-джойн с собственной целью не атомарен, лаг реплик протаскивает дубли. Приём теряет урок «разобрал и вставил», а ярлык со смыслом «первый носитель» хуже для обеих ролей поля: окну нужен последний носитель, и сырьё живо скорее у него.
- Склейка
(updated_at, snapshot_date)в одну колонку версии. Движок берёт одну колонку, склейка требует битовой упаковки. Робастнее к недисциплинированному источнику, но платит читаемостью DDL за защиту от сценариев, которых модельный мир не производит. - Склейка
(snapshot_date, updated_at). Путь отступления: если у источника появятся переиздания слепка одной даты, версия дорастает до этой формы. От правок без сдвига метки внутри одной даты не спасает и она.
Связанные решения
- ADR 0010 — версии заказа в ODS; здесь пересмотрена только колонка версии.
- Приём заказов — граница строгого приёма и четыре координаты строки.
- Модель заказа в DDS — читатель окна пересборки.
- Развилка, веер и два холодных ревью — #163.