-- DDS: объём пересборки заказов — с какого дня по какой собирает этот запуск. -- -- Параметров у дага нет: объём он выводит сам из состояния слоёв -- (docs/architecture/etl.md, «Объём прогона»). Ответ — две даты, границы -- непрерывного отрезка; список дней между ними разворачивает даг. Пустой -- источник даёт пустой ответ, и даг на нём останавливается с внятной ошибкой -- вместо зелёного прогона, собравшего ничего. -- -- Файл кончается без точки с запятой: даг читает его целиком и шлёт одним -- запросом, дописывая формат ответа, — точка с запятой сделала бы из этого -- мультизапрос, который ClickHouse не принимает. -- -- Объём складывается из двух частей. Первая — дышащее окно: дни, которые нёс -- последний слепок, то есть те, чьё состояние источник ещё может изменить. -- Вторая — хвост от первого дня, которого в слое нет: первая сборка застаёт -- слой пустым и собирает весь стартовый мир, а после разгона мира пачкой слой -- догоняет уехавший вперёд источник. -- -- **Хвост считается от первого недостающего дня, а не от последнего -- собранного.** Разница не косметическая: замены партиций идут отдельными -- задачами и падают порознь, поэтому упавший день оставляет дыру, пока -- соседние дни собираются дальше. Считай мы прогресс по максимуму собранного -- дня, дыра позади окна не заросла бы никогда, а прогоны продолжали бы -- зеленеть над щербатым слоем. Цена честного ответа — проход по дню заказа -- в ODS. -- -- Окно читается из ярлыка слепка, а не вычисляется вычитанием окна -- изменяемости: у выжившей строки snapshot_date — последний слепок, который -- нёс этот заказ (ADR 0014). Поэтому оба края окна знает сам источник, а -- копии его константы на стороне стенда больше нет. -- -- FINAL не нужен ни здесь, ни ниже: время рождения заказа у всех его версий -- одно, а строки последнего слепка несут максимум колонки версии — мерж -- оставляет как раз их. WITH (SELECT max(snapshot_date) FROM ods.order_dist) AS last_snapshot, ( SELECT min(toDate(toTimeZone(created_at, 'Europe/Samara'))) FROM ods.order_dist WHERE snapshot_date = last_snapshot ) AS window_start, ( SELECT max(toDate(toTimeZone(created_at, 'Europe/Samara'))) FROM ods.order_dist WHERE snapshot_date = last_snapshot ) AS window_end, -- GLOBAL обязателен: обе таблицы распределённые, и обычный NOT IN сверял -- бы дни источника с локальной частью слоя на каждом шарде — день, чьи -- заказы легли на соседний шард, показался бы недостающим. -- minOrNull отличает «дыр нет» от даты по умолчанию. ( SELECT minOrNull(day) FROM ( SELECT DISTINCT toDate(toTimeZone(created_at, 'Europe/Samara')) AS day FROM ods.order_dist ) WHERE day GLOBAL NOT IN (SELECT order_date FROM dds.order_dist) ) AS first_missing_day SELECT -- Дыр нет — начинаем с окна; есть — с первой из них, если она старше окна. least(window_start, coalesce(first_missing_day, window_start)) AS first_day, window_end AS last_day WHERE (SELECT count() FROM ods.order_dist) > 0