# Перепишите сборку заказов в DDS **Чему учит:** переписать пересборку заказов по состоянию слоев и доказать ее результат на пропущенном закрытом дне. ## Сначала прочитать - [проверки заказов](5-1-order-checks.md); - [разбор заказов в ODS](5-2-order-parsing.md); - [модель заказа DDS](../architecture/dds/order.md); - [дневной конвейер](../architecture/etl.md#объём-прогона); - [раскладку по шардам](../architecture/storage.md#раскладка-по-шардам). ## Граница задания Удалите готовый SQL и перепишите только три файла: - `sql/dds/order_scope.sql`; - `sql/dds/order_rebuild.sql`; - `sql/dds/order_replace.sql`. Даг `dds_transform`, DDL, донор, ODS и обе проверки из первого задания уже задают границу работы. Перед началом убедитесь, что пробные строки предыдущего задания удалены, а семь DQ дают зеленый результат. Поставьте `world_live` на паузу и дождитесь завершения активных прогонов. До завершения опыта не запускайте `world_live_day`, `world_initialize` и `world_recreate`. `world_next_day` понадобится только в явно указанном ниже случае. ## Договор с дагом Сохраните интерфейс `dds_transform`: | Файл | Вход | Результат | |---|---|---| | `order_scope.sql` | без параметров | одна строка `first_day`, `last_day` | | `order_rebuild.sql` | `{first_day:Date}`, `{last_day:Date}`, `{load_id:String}` | выбранные дни в `dds.order_stage_dist` | | `order_replace.sql` | день в `{{ params.day }}` | партиция дня в `dds.order_rep` | Объем состоит из текущего окна источника и хвоста от первого дня, который есть в ODS, но отсутствует в DDS. Текущее окно дают строки последнего `snapshot_date`: их минимальный и максимальный день заказа. Если источник пуст, `order_scope.sql` не возвращает строку. Если дыр нет, `first_day` равен началу окна. Если дыра старше, непрерывный отрезок начинается с нее. `order_rebuild.sql` сначала очищает все партиции `dds.order_stage_rep` на кластере, затем записывает в `dds.order_stage_dist` весь выбранный отрезок. Он читает текущее состояние `ods.order_v`, вычисляет `order_date`, переводит времена в `Europe/Samara`, разбирает `items` в три параллельных массива и ставит координаты запуска. Запись через Distributed должна завершиться до замены и использовать ключ целевой таблицы для раскладки строк. Способ собрать весь отрезок внутри файла выберите сами. `order_replace.sql` работает с локальными `dds.order_rep` и `dds.order_stage_rep` на всем кластере. `REPLACE PARTITION` переносит готовую дневную партицию из донора и оставляет донор на месте. Структура, ключи и политика хранения совпадают: DDL объявляет донор через `AS dds.order_rep`. ## Восстановите ближайший закрытый день Найдите последний по дате день DDS, который лежит левее начала текущего окна ODS. Это ближайший к окну закрытый день: его удаление показывает расширение объема на дыру без пересборки всей истории. Если такого дня еще нет, один раз продвиньте мир через `world_next_day`, дождитесь `orders_ingest`, затем запустите `etl_pipeline`. После этого снова зафиксируйте модельное время до конца опыта и выберите ближайший закрытый день. Сохраните для него число заказов и контрольную сумму деловых полей без `_load_id` и `_load_ts`. Удалите только его партицию: ```sql ALTER TABLE dds.order_rep ON CLUSTER clickstream_cluster DROP PARTITION 'YYYY-MM-DD'; ``` Сразу запустите `dq_check`, до восстановления партиции. Красный результат должны дать `orders_vs_ods` и `purchase_vs_orders_vs_dds`. `revenue_daily_vs_dds` тоже даст красный результат, если в партиции были оплаченные заказы. `classes_vs_inventory` останется зеленым: до запуска `dm_transform` эта проверка читает прежнюю витрину сверки. Запустите `etl_pipeline`. В группе `dds_transform.order` значение `first_day` должно совпасть с удаленным днем, а задачи `replace` - покрыть непрерывный отрезок от него до конца текущего окна. `first_day` ищите в строке про объем пересборки в журнале задачи `order.scope` дага `dds_transform`. После успешного прогона число заказов и контрольная сумма выбранного дня должны совпасть с сохраненными. ## Проверьте раскладку и повтор Напишите отдельный запрос к `dds.order_dist`, который получает фактическую ноду через `hostName()`, вычисляет ожидаемую по `cityHash64(order_id) % 2` и считает несовпадения по каждой ноде. Остаток 0 соответствует `clickhouse-01`, остаток 1 - `clickhouse-02`. На обеих нодах число строк на чужом шарде должно быть нулевым. Счетные DQ читают Distributed-таблицы и такую ошибку раскладки не обнаруживают. Запустите `etl_pipeline` еще раз при неизменном модельном времени. Группа заказов снова должна пересобрать текущее изменяемое окно. Контрольные суммы деловых полей остаются прежними, а все семь проверок дают зеленый результат. ## Когда можно остановиться Удаленная партиция восстановлена, ее число строк и контрольная сумма совпали с исходными. На чужих шардах строк нет, повторный прогон не изменил деловые данные, семь DQ зелены. Если до упражнения `world_live` работал, теперь его можно снять с паузы. ## Приемка - [ ] переписаны только три файла `sql/dds/order_*.sql`, инфраструктура упражнения не изменена; - [ ] удален ближайший к окну закрытый день, а красный DQ зафиксирован до восстановления; - [ ] `first_day` начался с удаленного дня, задачи замены покрыли непрерывный отрезок до конца окна; - [ ] число заказов и контрольная сумма восстановленного дня совпали; - [ ] `order_items_total`, `orders_vs_ods` и пять прежних DQ зелены; - [ ] проверка `hostName()` не нашла заказов DDS на чужом шарде; - [ ] повторный `etl_pipeline` сохранил деловые данные и зеленые проверки.