Звено DQ конвейера и dm.dq_summary #158

Closed
opened 2026-08-25 15:01:10 +03:00 by ddmitry · 3 comments
Owner

Part of #6.

Цель

Контур качества конвейера: даг dq_check звеном etl_pipeline после DM
и сводка dm.dq_summary зерном «день данных × проверка» с заменой
партиций проверенных дней. Учебный результат (dm.md): контроль качества в
бою — по расписанию, с историей, рядом с трансформациями; звено — не
только сторож, но и экспонат.

Что войдёт

  • DDL dm.dq_summary по конвенциям storage.md (_rep / _dist / _v):
    партиция — день данных; строка отвечает «каково качество дня сейчас»;
    состав строки (имя проверки, стороны сверки, расхождение, статус) —
    решить с DDL по рамке dm.md; грант bi_reader на _v.
  • Даг dq_check по образцу слойных: без расписания, один активный
    прогон; встаёт звеном etl_pipeline после dm_transform; имя
    задачи — имя проверки:
    • sessions_vs_reference — счётная сверка сессий с эталоном VisitID,
      зерно «кука × день», эталон из ods.event_v (session.md);
    • classes_vs_inventory — счётчики классов сверки против описи
      data/world-inventory.json: только дни, которые опись покрывает и
      чьё окно закрыто; дни, прожитые сверх стартового мира, опись не
      описывает — они не проверяются. Проверка техническая, не урок DQ:
      её оракул — опись генератора, у настоящего магазина такого нет
      (резолюция в #157; dm.md «Звено DQ», термин в CONTEXT.md). Звеном
      остаётся и в dq_summary пишет наравне с остальными;
    • marts_vs_dds — счётные сверки каждой витрины стенда с её
      DDS-источниками; точный список проверок зафиксировать в доке слоя DM
      той же правкой.
  • Расхождение — красная задача: звено честно валит конвейер.
  • Прогон заменяет партиции проверенных дней — история прогонов живёт в
    Airflow, не в таблице.

Границы

  • Домашки менти звено не проверяет — у них даг самопроверки (тикет ТЗ).
  • Порогов и «жёлтых» статусов нет (dm.md, отклонённые варианты).
  • dq_errors_daily — учебная витрина, не проверка контура.

Кому что

Проверочные запросы — Opus: каждая проверка — урок; у технической classes_vs_inventory урок другой — честная табличка комментарием в SQL. Codex — прогон на
искусственном расхождении по брифу: порча — в партиции за границей оси
мира, после опыта прибраться (testing.md).

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

  • docs/architecture/dm.md — «dm.dq_summary», «Звено DQ конвейера», это
    спека тикета;
  • docs/architecture/dds/session.md — «Сверка с эталоном»;
  • docs/architecture/orders/inventory.md и мастер-спека §8 — что обещает
    опись;
  • docs/architecture/etl.md — место звена;
  • docs/architecture/testing.md — за опытами прибираются.

Проверка

  • make lint; на стенде make smoke, make check-clickhouse и
    make check-services (родился даг) зелёные.
  • Полный прогон etl_pipeline на честном мире: звено зелёное; в
    dq_summary — строка на каждую пару «проверенный день × проверка»,
    без красных.
  • Каждая из трёх проверок продемонстрирована красной на своём
    искусственном расхождении и валит конвейер; строка дня называет
    расхождение.
  • Повторный прогон после починки заменяет партицию дня: в таблице —
    текущее качество, не история попыток.
Part of #6. ## Цель Контур качества конвейера: даг `dq_check` звеном `etl_pipeline` после DM и сводка `dm.dq_summary` зерном «день данных × проверка» с заменой партиций проверенных дней. Учебный результат (dm.md): контроль качества в бою — по расписанию, с историей, рядом с трансформациями; звено — не только сторож, но и экспонат. ## Что войдёт - DDL `dm.dq_summary` по конвенциям storage.md (`_rep` / `_dist` / `_v`): партиция — день данных; строка отвечает «каково качество дня сейчас»; состав строки (имя проверки, стороны сверки, расхождение, статус) — решить с DDL по рамке dm.md; грант `bi_reader` на `_v`. - Даг `dq_check` по образцу слойных: без расписания, один активный прогон; встаёт звеном `etl_pipeline` после `dm_transform`; имя задачи — имя проверки: - `sessions_vs_reference` — счётная сверка сессий с эталоном `VisitID`, зерно «кука × день», эталон из `ods.event_v` (session.md); - `classes_vs_inventory` — счётчики классов сверки против описи `data/world-inventory.json`: только дни, которые опись покрывает и чьё окно закрыто; дни, прожитые сверх стартового мира, опись не описывает — они не проверяются. Проверка техническая, не урок DQ: её оракул — опись генератора, у настоящего магазина такого нет (резолюция в #157; dm.md «Звено DQ», термин в CONTEXT.md). Звеном остаётся и в `dq_summary` пишет наравне с остальными; - `marts_vs_dds` — счётные сверки каждой витрины стенда с её DDS-источниками; точный список проверок зафиксировать в доке слоя DM той же правкой. - Расхождение — красная задача: звено честно валит конвейер. - Прогон заменяет партиции проверенных дней — история прогонов живёт в Airflow, не в таблице. ## Границы - Домашки менти звено не проверяет — у них даг самопроверки (тикет ТЗ). - Порогов и «жёлтых» статусов нет (dm.md, отклонённые варианты). - `dq_errors_daily` — учебная витрина, не проверка контура. ## Кому что Проверочные запросы — Opus: каждая проверка — урок; у технической `classes_vs_inventory` урок другой — честная табличка комментарием в SQL. Codex — прогон на искусственном расхождении по брифу: порча — в партиции за границей оси мира, после опыта прибраться (testing.md). ## Сначала прочитать - docs/architecture/dm.md — «dm.dq_summary», «Звено DQ конвейера», это спека тикета; - docs/architecture/dds/session.md — «Сверка с эталоном»; - docs/architecture/orders/inventory.md и мастер-спека §8 — что обещает опись; - docs/architecture/etl.md — место звена; - docs/architecture/testing.md — за опытами прибираются. ## Проверка - [x] `make lint`; на стенде `make smoke`, `make check-clickhouse` и `make check-services` (родился даг) зелёные. - [x] Полный прогон `etl_pipeline` на честном мире: звено зелёное; в `dq_summary` — строка на каждую пару «проверенный день × проверка», без красных. - [x] Каждая из трёх проверок продемонстрирована красной на своём искусственном расхождении и валит конвейер; строка дня называет расхождение. - [x] Повторный прогон после починки заменяет партицию дня: в таблице — текущее качество, не история попыток.
ddmitry added a new dependency 2026-08-25 15:01:28 +03:00
Author
Owner

Постановка дополнена по резолюции развилки из #157: classes_vs_inventory явно помечена технической проверкой (оракул — опись генератора, за урок DQ не выдаётся), остаётся звеном и пишет в dq_summary. Правки словаря — dm.md «Звено DQ», §8 мастер-спеки, inventory.md, термин «Техническая проверка» в CONTEXT.md — едут веткой 157.

Постановка дополнена по резолюции развилки из #157: `classes_vs_inventory` явно помечена технической проверкой (оракул — опись генератора, за урок DQ не выдаётся), остаётся звеном и пишет в `dq_summary`. Правки словаря — dm.md «Звено DQ», §8 мастер-спеки, inventory.md, термин «Техническая проверка» в CONTEXT.md — едут веткой 157.
Author
Owner

Аудит готовности постановки — 27 августа 2026

Сверено с живым Gitea и checkout: main / origin/main / удалённый main совпадают на 4e60aaf; открытых PR и рабочих удалённых веток нет. #158 свободна, нативные блокеры #155, #156 и #157 закрыты; необходимая витрина #154 тоже уже в main.

Механика источников описана достаточно: сессии сравниваются с uniqExact(VisitID) в зерне «кука × день»; опись доступна Airflow по /opt/airflow/world/data/world-inventory.json и покрывает только закрытые дни; три стендовые витрины и их прямые DDS-агрегаты уже приняты в #154/#155.

Для холодной реализации постановка пока недостаточна: не определён публичный контракт dm.dq_summary_v. Фраза «день × проверка; стороны, расхождение, статус» не отвечает, как одной строкой представить:

  • расхождения по кукам у sessions_vs_reference без взаимной компенсации;
  • три счётчика классов у classes_vs_inventory;
  • несколько витрин, измерений и мер внутри marts_vs_dds.

Веер вариантов:

  1. Короткая сводка: ровно одна строка «день × одна из трёх проверок»; хранить число элементов на обеих сторонах, число несовпавших элементов и status, а конкретные ключи и значения писать в журнал задачи.
  2. Детальный протокол: углубить зерно до «день × проверка × подобъект/метрика» и хранить значения сторон. Диагностика сильнее, но договор, типы и число строк заметно сложнее.
  3. Отдельные таблицы результатов для трёх проверок и объединяющее представление. Разные формы выражаются честно, но появляется физическая сложность без нового урока.
  4. Только красные задачи и журналы, без dq_summary. Это проще, но отвергает уже принятое обещание слоя.
  5. Оставить выбор исполнителю. Это лишь переносит публичный договор в код-ревью и для готового тикета не подходит.

Рекомендация: вариант 1. Он сохраняет принятое зерно и три имени задач, а таблица остаётся именно сводкой. Единица сравнения определяется запросом: кука для сессий, класс для описи, проверяемая мера/ключ для витрин; несовпадения между элементами не компенсируются.

Стык красной задачи и REPLACE PARTITION решается без all_done, в рамках ADR 0003: проверки сначала собирают общий донор; общая публикация заменяет дневные партиции; затем три задачи sessions_vs_reference, classes_vs_inventory и marts_vs_dds читают уже опубликованные строки и краснеют при ненулевом числе несовпадений. Ошибка сборки или публикации не затирает прежнюю сводку, а расхождение уже видно в dq_summary до падения именованной задачи.

До подтверждения владельцем вариант 1 — предложение, не решение. После выбора стоит отдельным комментарием закрепить точные колонки и смысл единицы сравнения; код и docs пока не трогать.

## Аудит готовности постановки — 27 августа 2026 Сверено с живым Gitea и checkout: main / origin/main / удалённый main совпадают на 4e60aaf; открытых PR и рабочих удалённых веток нет. #158 свободна, нативные блокеры #155, #156 и #157 закрыты; необходимая витрина #154 тоже уже в main. Механика источников описана достаточно: сессии сравниваются с uniqExact(VisitID) в зерне «кука × день»; опись доступна Airflow по /opt/airflow/world/data/world-inventory.json и покрывает только закрытые дни; три стендовые витрины и их прямые DDS-агрегаты уже приняты в #154/#155. Для холодной реализации постановка пока недостаточна: не определён публичный контракт dm.dq_summary_v. Фраза «день × проверка; стороны, расхождение, статус» не отвечает, как одной строкой представить: - расхождения по кукам у sessions_vs_reference без взаимной компенсации; - три счётчика классов у classes_vs_inventory; - несколько витрин, измерений и мер внутри marts_vs_dds. Веер вариантов: 1. Короткая сводка: ровно одна строка «день × одна из трёх проверок»; хранить число элементов на обеих сторонах, число несовпавших элементов и status, а конкретные ключи и значения писать в журнал задачи. 2. Детальный протокол: углубить зерно до «день × проверка × подобъект/метрика» и хранить значения сторон. Диагностика сильнее, но договор, типы и число строк заметно сложнее. 3. Отдельные таблицы результатов для трёх проверок и объединяющее представление. Разные формы выражаются честно, но появляется физическая сложность без нового урока. 4. Только красные задачи и журналы, без dq_summary. Это проще, но отвергает уже принятое обещание слоя. 5. Оставить выбор исполнителю. Это лишь переносит публичный договор в код-ревью и для готового тикета не подходит. Рекомендация: вариант 1. Он сохраняет принятое зерно и три имени задач, а таблица остаётся именно сводкой. Единица сравнения определяется запросом: кука для сессий, класс для описи, проверяемая мера/ключ для витрин; несовпадения между элементами не компенсируются. Стык красной задачи и REPLACE PARTITION решается без all_done, в рамках ADR 0003: проверки сначала собирают общий донор; общая публикация заменяет дневные партиции; затем три задачи sessions_vs_reference, classes_vs_inventory и marts_vs_dds читают уже опубликованные строки и краснеют при ненулевом числе несовпадений. Ошибка сборки или публикации не затирает прежнюю сводку, а расхождение уже видно в dq_summary до падения именованной задачи. До подтверждения владельцем вариант 1 — предложение, не решение. После выбора стоит отдельным комментарием закрепить точные колонки и смысл единицы сравнения; код и docs пока не трогать.
Author
Owner

Принятая резолюция: договор DQ-сводки

Владелец подтвердил направление 27 августа 2026 года. Предыдущий комментарий с веером вариантов считать обсуждением; ниже — решение.

Чему учится менти

Проверка формулируется на естественном деловом зерне и возвращает нарушившие договор ключи. Общие итоги не имеют права скрыть две компенсировавшие друг друга ошибки. Сводка отвечает, что сломано сейчас; Airflow отвечает, в каком прогоне это случилось.

Публичный договор

Зерно dm.dq_summary_v — «день данных × атомарная проверка». Деловые колонки:

  • data_date — проверяемый день данных;
  • check_name — устойчивое имя одного проверяемого инварианта;
  • reference_rows — число деловых ключей на эталонной стороне;
  • actual_rows — число деловых ключей в проверяемом объекте;
  • failed_rows — число ключей, которые отсутствуют на одной стороне либо имеют разные деловые значения;
  • status — pass при failed_rows = 0, иначе fail.

К ним добавляются обычные служебные координаты собравшего запуска. Порогов и промежуточного статуса нет. Публичное _v перечисляет колонки явно; bi_reader читает только его.

Имена атомарных проверок:

  • sessions_vs_reference;
  • classes_vs_inventory;
  • revenue_daily_vs_dds;
  • purchase_vs_orders_vs_dds;
  • daily_traffic_vs_dds.

Единица сравнения

  • sessions_vs_reference: ключ — client_id внутри дня; эталонное значение — uniqExact(VisitID) из ods.event_v, фактическое — число строк dds.session_v. failed_rows считает куки с разными числами, поэтому лишняя сессия одной куки не компенсирует потерянную сессию другой.
  • classes_vs_inventory: ключ — mismatch_class; эталонное значение — счётчик из orders строки описи, фактическое — число заказов класса в dm.purchase_vs_orders_v. Проверяются только match, cancelled и amount_delta, только закрытые дни, покрытые описью. Это техническая растяжка пути, не доказательство корректности генератора.
  • revenue_daily_vs_dds: ключ — product_category внутри дня; деловые значения строки витрины сверяются с прямым агрегатом dds.order_v по принятому договору revenue_daily.
  • purchase_vs_orders_vs_dds: ключ — order_id внутри дня; деловые значения строки сверки сопоставляются с независимо полученным результатом по её DDS-источникам.
  • daily_traffic_vs_dds: ключ — день; visitors и known_users сверяются с прямыми агрегатами dds.session_v и dds.identity_map_v.

Каждый запуск проверяет всю покрытую историю стенда. Область всегда выводится из объединения сторон, чтобы полностью потерянный день не исчез из проверки: для сессий — завершённые дни из ods.event_v и dds.session_v; для описи — покрытые закрытые дни; для каждой витрины — дни её DDS-источников и самой витрины. На учебном объёме это быстро и не требует протаскивать между дагами внутренний список изменённых партиций. Производственное сокращение области проверки можно обсуждать отдельно, когда появится цена полного прогона.

Диагностика

Сводка остаётся короткой. Красная задача печатает малый ограниченный срез несовпавших ключей вместе со значениями обеих сторон; полный список воспроизводится тем же SQL проверки. Отдельную постоянную таблицу нарушивших строк, историю DQ-прогонов, сроки хранения и обработку чувствительных образцов сейчас не заводим: это отдельный урок, у которого пока нет читателя.

marts_vs_dds остаётся видимым семейством в графе, но становится группой трёх атомарных проверок витрин. Так граф показывает общий сюжет, а красная задача и check_name сразу называют сломавшуюся витрину.

Публикация и красный запуск

Все запросы сначала собирают общий донор. Только после успешной сборки общая задача заменяет партиции проверенных дней в dq_summary. Затем именованные задачи-утверждения читают уже опубликованные строки и краснеют при failed_rows > 0.

Так расхождение успевает попасть в dq_summary до отказа, а ошибка самой сборки или публикации не заменяет текущую сводку неполным результатом. all_done не нужен; правило ADR 0003 о красном конце графа сохраняется.

Следующий успешный прогон заменяет дневные партиции и убирает прежнее расхождение. В ClickHouse хранится текущее качество дня, история попыток остаётся в Airflow.

Не входит

  • пороги, warn и жёлтые состояния;
  • универсальные JSON-поля со значениями произвольных типов;
  • отдельные физические таблицы на семейство проверок;
  • постоянное хранение всех нарушивших строк;
  • история прогонов в dq_summary;
  • DQ домашек менти.

С этой резолюцией архитектурных решений за исполнителем не остаётся. Итоговые формулировки и точный список сравниваемых деловых колонок переносятся в dm.md той же реализацией, как требует постановка.

## Принятая резолюция: договор DQ-сводки Владелец подтвердил направление 27 августа 2026 года. Предыдущий комментарий с веером вариантов считать обсуждением; ниже — решение. ### Чему учится менти Проверка формулируется на естественном деловом зерне и возвращает нарушившие договор ключи. Общие итоги не имеют права скрыть две компенсировавшие друг друга ошибки. Сводка отвечает, что сломано сейчас; Airflow отвечает, в каком прогоне это случилось. ### Публичный договор Зерно dm.dq_summary_v — «день данных × атомарная проверка». Деловые колонки: - data_date — проверяемый день данных; - check_name — устойчивое имя одного проверяемого инварианта; - reference_rows — число деловых ключей на эталонной стороне; - actual_rows — число деловых ключей в проверяемом объекте; - failed_rows — число ключей, которые отсутствуют на одной стороне либо имеют разные деловые значения; - status — pass при failed_rows = 0, иначе fail. К ним добавляются обычные служебные координаты собравшего запуска. Порогов и промежуточного статуса нет. Публичное _v перечисляет колонки явно; bi_reader читает только его. Имена атомарных проверок: - sessions_vs_reference; - classes_vs_inventory; - revenue_daily_vs_dds; - purchase_vs_orders_vs_dds; - daily_traffic_vs_dds. ### Единица сравнения - sessions_vs_reference: ключ — client_id внутри дня; эталонное значение — uniqExact(VisitID) из ods.event_v, фактическое — число строк dds.session_v. failed_rows считает куки с разными числами, поэтому лишняя сессия одной куки не компенсирует потерянную сессию другой. - classes_vs_inventory: ключ — mismatch_class; эталонное значение — счётчик из orders строки описи, фактическое — число заказов класса в dm.purchase_vs_orders_v. Проверяются только match, cancelled и amount_delta, только закрытые дни, покрытые описью. Это техническая растяжка пути, не доказательство корректности генератора. - revenue_daily_vs_dds: ключ — product_category внутри дня; деловые значения строки витрины сверяются с прямым агрегатом dds.order_v по принятому договору revenue_daily. - purchase_vs_orders_vs_dds: ключ — order_id внутри дня; деловые значения строки сверки сопоставляются с независимо полученным результатом по её DDS-источникам. - daily_traffic_vs_dds: ключ — день; visitors и known_users сверяются с прямыми агрегатами dds.session_v и dds.identity_map_v. Каждый запуск проверяет всю покрытую историю стенда. Область всегда выводится из объединения сторон, чтобы полностью потерянный день не исчез из проверки: для сессий — завершённые дни из ods.event_v и dds.session_v; для описи — покрытые закрытые дни; для каждой витрины — дни её DDS-источников и самой витрины. На учебном объёме это быстро и не требует протаскивать между дагами внутренний список изменённых партиций. Производственное сокращение области проверки можно обсуждать отдельно, когда появится цена полного прогона. ### Диагностика Сводка остаётся короткой. Красная задача печатает малый ограниченный срез несовпавших ключей вместе со значениями обеих сторон; полный список воспроизводится тем же SQL проверки. Отдельную постоянную таблицу нарушивших строк, историю DQ-прогонов, сроки хранения и обработку чувствительных образцов сейчас не заводим: это отдельный урок, у которого пока нет читателя. marts_vs_dds остаётся видимым семейством в графе, но становится группой трёх атомарных проверок витрин. Так граф показывает общий сюжет, а красная задача и check_name сразу называют сломавшуюся витрину. ### Публикация и красный запуск Все запросы сначала собирают общий донор. Только после успешной сборки общая задача заменяет партиции проверенных дней в dq_summary. Затем именованные задачи-утверждения читают уже опубликованные строки и краснеют при failed_rows > 0. Так расхождение успевает попасть в dq_summary до отказа, а ошибка самой сборки или публикации не заменяет текущую сводку неполным результатом. all_done не нужен; правило ADR 0003 о красном конце графа сохраняется. Следующий успешный прогон заменяет дневные партиции и убирает прежнее расхождение. В ClickHouse хранится текущее качество дня, история попыток остаётся в Airflow. ### Не входит - пороги, warn и жёлтые состояния; - универсальные JSON-поля со значениями произвольных типов; - отдельные физические таблицы на семейство проверок; - постоянное хранение всех нарушивших строк; - история прогонов в dq_summary; - DQ домашек менти. С этой резолюцией архитектурных решений за исполнителем не остаётся. Итоговые формулировки и точный список сравниваемых деловых колонок переносятся в dm.md той же реализацией, как требует постановка.
ddmitry self-assigned this 2026-08-27 13:32:03 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#158