Files
airflow-greenplum/docs/design/assignment_design.md
ddadminandClaude Sonnet 4.6 4e9d8f5a1c feat(dds): перенесён lookup аэропортов на ods.routes, обновлена документация задания
- Зачем:
  - fact_flight_sales брал airport_sk через dds.dim_routes (студенческое задание SCD2);
    на ветке main без реализованного dim_routes все airport_sk были NULL,
    и эталонные витрины sales_report/airport_traffic становились бесполезны.
- Что:
  - fact_flight_sales_load.sql: добавлен subquery ods_rte (ROW_NUMBER по ods.routes),
    dep/arr airport_sk теперь через ods.routes (эталон); airplane_sk — через dim_routes (point-in-time).
  - analyst_spec.md: удалены STG-задания (1.1–1.3) и ods.routes (2.3), добавлены секции
    «Почему STG уже реализован» и «Пересчёт факта», исправлен ODS intro, перенумерованы части.
  - assignment_design.md: обновлены таблицы эталон/задание, порядок выполнения; убраны
    validate_stg и check_ods_routes_rowcount из структуры валидационного DAG.
  - db_schema.md: статус уточнён («реализовано в ветке solution»), добавлена оговорка
    о заглушках на main, DQ-контракт дополнен примечанием.
- Проверка:
  - make test → 4 passed, 9 skipped.
  - ручная проверка: TRUNCATE dds.fact_flight_sales → перезапуск DDS DAG →
    COUNT(departure_airport_sk) должен равняться COUNT(*).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 10:39:19 +03:00

10 KiB

Дизайн курсового задания

Тактические решения по нарезке задания, порядку выполнения и самопроверке. Стратегию и контекст см. в PRD.md.


1. Эталонный срез: витрина sales_report

Эталоном выбрана витрина dm.sales_report и вся её цепочка вниз по слоям.

Почему sales_report:

  • Покрывает SCD1 (airports, tariffs), HWM-инкремент, fact load
  • Богатая денормализация — хороший образец для подражания
  • Средняя сложность — не пугает, но и не тривиальна

Эталонные таблицы (даны студенту)

Слой Таблицы
DM sales_report
DDS fact_flight_sales, dim_airports (SCD1), dim_tariffs (SCD1), dim_calendar
ODS bookings, tickets, segments, flights, boarding_passes, airports, routes
STG весь слой: bookings, tickets, segments, flights, boarding_passes, airports, airplanes, seats, routes

Задание студенту

Слой Таблицы Что нового для студента
ODS airplanes, seats Практика TRUNCATE+INSERT по аналогии с эталоном
DDS dim_airplanes (SCD1), dim_passengers (SCD1), dim_routes (SCD2) SCD2 — ключевой вызов курсовой
DM airport_traffic, monthly_overview, route_performance, passenger_loyalty Разная сложность (от простой к сложной)

2. Рекомендуемый порядок выполнения

Студенту рекомендуется (но не обязательно) двигаться в таком порядке:

  1. ODS (airplanes, seats) — практика TRUNCATE+INSERT
  2. DDS dim_airplanes, dim_passengers (SCD1) — новые измерения
  3. DDS dim_routes (SCD2) — ключевой вызов
  4. DM airport_traffic — простая витрина, похожа на sales_report
  5. DM route_performance — TRUNCATE+INSERT, SCD2-агрегация по BK
  6. DM monthly_overview — двухуровневая агрегация
  7. DM passenger_loyalty — самая сложная, пересчёт истории

Порядок выстроен от простого к сложному. Каждый шаг опирается на опыт предыдущего.


3. SCD2: подход «рецепт без готового SQL»

Реализация dim_routes (SCD2) — ключевой вызов курсовой. Студент делает это самостоятельно, но ТЗ содержит пошаговую подсказку:

  1. Алгоритм SCD2 текстом (без SQL):
    • Вычисли hashdiff по набору атрибутов (атрибуты перечислены в ТЗ)
    • Найди строки, у которых hashdiff изменился
    • Закрой старую версию (valid_to = текущая_дата)
    • Вставь новую версию (valid_from = текущая_дата, valid_to = NULL)
  2. Формула hashdiff: md5(concat_ws('|', field1, field2, ...))
  3. Ссылка на naming_conventions.md (поля valid_from, valid_to, hashdiff)
  4. Напоминание: полуоткрытый интервал [valid_from, valid_to)
  5. Если застрял — ветка solution

Самостоятельная реализация — ключ к запоминанию. SCD2 — обязательный вопрос на собеседованиях DE, и студент должен уметь объяснить его на основе собственного опыта.


4. Валидационный DAG (bookings_validate)

Отдельный DAG для самопроверки студента. Запускается вручную в Airflow UI после реализации заданий. Таски сгруппированы по слоям — студент видит, где именно проблема. Дополнительный бонус — практика чтения логов Airflow.

Примерная структура тасков

bookings_validate
├── validate_ods
│   ├── check_ods_airplanes_rowcount    (ODS >= STG по кол-ву уникальных BK)
│   ├── check_ods_seats_rowcount
│   └── check_ods_no_null_pks           (PK not null)
├── validate_dds
│   ├── check_dim_airplanes_exists
│   ├── check_dim_passengers_exists
│   ├── check_dim_routes_scd2           (valid_from/valid_to корректны)
│   └── check_dim_routes_no_gaps        (нет «дыр» в версиях SCD2)
└── validate_dm
    ├── check_airport_traffic_exists
    ├── check_monthly_overview_exists
    ├── check_route_performance_exists
    └── check_passenger_loyalty_exists

Реализация

  • PostgresOperator + SQL-скрипты в sql/validate/
  • Каждый SQL-скрипт выполняет SELECT и бросает исключение (через DO $$ ... RAISE EXCEPTION ... $$), если проверка не пройдена
  • Сообщения об ошибках — дружелюбные, с подсказкой что делать дальше

Ключевые проверки

  • Таблицы существуют и содержат данные
  • PK не содержат NULL
  • SCD2: valid_to IS NULL для текущих версий, нет перекрытий интервалов
  • Кросс-слойная консистентность (row count ODS vs STG)
  • DM-витрины содержат данные за загруженные дни

Активная проверка SCD2 (check_dim_routes_scd2)

Справочник bookings.routes в демо-базе статичен — маршруты не меняются между запусками генератора. Поэтому при обычном прогоне пайплайна студент никогда не увидит, как SCD2 закрывает старую версию и создаёт новую.

Чтобы проверить корректность реализации, таск check_dim_routes_scd2 должен быть активным (не только читать, но и тестировать загрузку):

  1. Сохранить текущее состояние ods.routes и dds.dim_routes (temp-таблицы).
  2. Вставить в ods.routes тестовый маршрут с изменённым атрибутом (например, scheduled_timedeparture_time сдвинут на 1 час).
  3. Вызвать студенческий SQL загрузки dim_routes (sql/dds/dim_routes_load.sql).
  4. Проверить результат:
    • Старая версия маршрута закрыта (valid_to IS NOT NULL).
    • Новая версия открыта (valid_to IS NULL, hashdiff отличается).
    • Нет «дыр» между valid_to старой и valid_from новой версии.
  5. Откатить изменения: восстановить ods.routes и dds.dim_routes из сохранённых temp-таблиц.

Это единственный способ гарантировать, что SCD2 работает, без мутации источника (что сломало бы генератор continue()).


5. Формат ТЗ от аналитика

Файл: docs/assignment/analyst_spec.md (или несколько файлов по слоям).

Для каждой таблицы-задания документ содержит:

  • Имя таблицы и целевая схема (stg / ods / dds / dm)
  • Описание — что хранит таблица, бизнес-смысл
  • Список полей с типами и описанием
  • Маппинг источников — откуда берётся каждое поле
  • Бизнес-правила и фильтры (если есть)
  • Тип историзации (SCD1 / SCD2 / snapshot / append)
  • Гранулярность (одна строка = ?)
  • Distribution key (подсказка или задание на выбор)

Формат — приближен к реальным ТЗ, которые студент встретит на работе.


6. Бэклог: идеи для будущих итераций

PXF-практикум (замена STG-заданий)

После переноса всего STG-слоя в эталон (см. docs/plans/2026-03-11_routes-to-reference.md) студенты не практикуются в создании PXF external tables и загрузке сырых данных. Это важный навык для DE — нужно компенсировать отдельным заданием.

Варианты:

  • Новый источник данных: подключить CSV/JSON-файл (например, справочник городов или курсов валют) через PXF и загрузить в отдельную STG-таблицу
  • Мини-задание «подключи внешний справочник»: студент создаёт PXF external table для существующей таблицы источника и сравнивает результат с эталонной STG
  • Лабораторная работа: отдельное упражнение на создание external table + сравнение форматов (TEXT vs CUSTOM), профилей PXF, обработку типов