- Зачем: - необходим единый документ с требованиями для разработки ETL-процессов. - Что: - создан файл docs/internal/PRD.md с описанием архитектуры, требований к данным и сроков. - Проверка: - просмотр файла docs/internal/PRD.md.
23 KiB
PRD: Greenplum Bookings DWH
Курсовая работа для курса DE Roadmap. Статус: ЧЕРНОВИК v0.1 | Дата: 2026-03-08
1. Видение продукта
Greenplum Bookings DWH — учебный стенд, на котором студент самостоятельно строит end-to-end ETL-пайплайн: от базы-источника до аналитических витрин.
Стенд имитирует реальную рабочую задачу Data-инженера:
- Есть «боевая» система-источник (bookings-db), в которой каждый день появляются новые данные — как в жизни, без ограниченного объёма.
- Есть DWH на Greenplum с классическими слоями (STG → ODS → DDS → DM).
- Есть Airflow, оркестрирующий загрузку.
- Есть ТЗ от «аналитика» с описанием ожидаемых таблиц и маппингов.
Студент получает частично реализованный пайплайн (эталонный вертикальный срез) и дореализует остальное по ТЗ — SQL-скрипты и таски в DAG.
Почему именно bookings?
Домен бронирования авиабилетов выбран не ради предметной области, а благодаря
генератору данных: каждый вызов make bookings-generate-day создаёт новый день
с реалистичным объёмом. Это даёт бесконечный поток инкрементальных данных —
как в настоящей production-системе.
2. Целевая аудитория и пререквизиты
Кто: студенты курса DE Roadmap, дошедшие до раздела «Курсовая работа».
Что уже умеют (к моменту старта):
- Git: ветки, PR, merge, GitFlow
- SQL: JOIN, CTE, оконные функции, планы запросов, моделирование (3NF, звезда, SCD)
- Python: скрипты, pandas, базовое ООП
- Docker: запуск контейнеров, логи, docker-compose
- Airflow: понятие DAG, операторы, зависимости, UI, логи
- Greenplum: распределение по сегментам, skew, EXPLAIN, отличие от Postgres
Уровень: уверенный джун, готовящийся к первым собеседованиям.
3. Учебные результаты (Learning Outcomes)
После выполнения курсовой студент умеет:
- Проектировать и реализовывать ETL-пайплайн по слоям DWH (STG → ODS → DDS → DM) на реальном стеке Airflow + Greenplum.
- Читать ТЗ от аналитика (маппинги, описания таблиц) и превращать его в работающий SQL + DAG.
- Писать идемпотентные загрузки с инкрементальностью (HWM, batch_id, delete+insert), понимая, почему в Greenplum не используется MERGE.
- Реализовывать SCD1/SCD2 и объяснять, когда что применяется.
- Настраивать и проверять Data Quality — понимает, зачем DQ-проверки и как их встроить в пайплайн.
- Работать с Greenplum как с MPP: выбирать distribution key, понимать heap vs AO, читать планы запросов.
- Оформить проект как портфолио — репозиторий пригоден для упаковки в резюме как реальный опыт работы с Airflow и Greenplum.
4. Скоуп
В скоупе (In Scope)
| Компонент | Описание |
|---|---|
| Источник данных | bookings-db (Postgres) с генератором дней |
| DWH | Greenplum, 4 слоя: STG, ODS, DDS, DM |
| Оркестрация | Apache Airflow (PostgresOperator + SQL-файлы) |
| Федеративный доступ | PXF (чтение из Postgres в Greenplum) |
| Инфраструктура | Docker Compose (полный стенд в одной команде) |
| Data Quality | DQ-проверки, встроенные в DAG |
| Документация | README, ТЗ, design docs, naming conventions |
Вне скоупа (Out of Scope)
| Что | Почему |
|---|---|
| Kafka / стриминг | Отдельный стенд в курсе |
| BI-инструменты | Фокус на ETL, не на визуализации |
| CI/CD | Избыточно для курсовой |
| Spark / Trino / dbt | Отдельные стенды в курсе |
| Второй источник | Усложнение без пропорциональной учебной ценности |
| CSV-пайплайн | Вынести в airflow-manual |
| Облачная инфраструктура | Всё локально, через Docker |
5. Архитектура стенда
Сервисы (Docker Compose)
bookings-db (Postgres 16) ──PXF──> Greenplum 6.27
├── stg.* (стейджинг)
├── ods.* (операционное хранилище)
├── dds.* (детальное хранилище)
└── dm.* (витрины)
pgmeta (Postgres 16) ─────────────> Airflow (webserver + scheduler)
Слои DWH
| Слой | Назначение | Паттерн загрузки | Кол-во таблиц |
|---|---|---|---|
| STG | Зеркало источника | TRUNCATE + INSERT (batch) | 9 |
| ODS | Нормализованное хранилище | SCD1 UPSERT | 9 |
| DDS | Измерения + факты (Kimball) | SCD1/SCD2 + fact load | 7 (6D + 1F) |
| DM | Аналитические витрины | HWM-инкремент | 5 |
Сущности
| STG / ODS | DDS | DM |
|---|---|---|
| bookings | dim_airports | airport_traffic |
| tickets | dim_airplanes | monthly_overview |
| airports | dim_passengers | passenger_loyalty |
| airplanes | dim_routes (SCD2) | route_performance |
| routes | dim_calendar | sales_report |
| seats | dim_tariffs | |
| flights | fact_flight_sales | |
| segments | ||
| boarding_passes |
6. Педагогическая модель
Принцип: «Эталонный срез + ТЗ»
Студент получает репозиторий, в котором:
-
Эталонный вертикальный срез — полностью реализованная цепочка для одной витрины DM и всех её источников вниз по слоям (STG → ODS → DDS → DM). Это — образец, на который студент ориентируется.
-
ТЗ от аналитика — Markdown-документ с описанием остальных таблиц: маппинги полей, бизнес-правила, ожидаемая гранулярность, тип SCD.
-
Частично готовый DAG — Python-файлы DAG с реализованными тасками эталонного среза. Студент добавляет свои таски по аналогии.
-
DQ-проверки — готовые проверки, которые студент запускает для самоконтроля.
Что делает студент
- Пишет DDL для назначенных таблиц (
*_ddl.sql) - Пишет SQL-загрузки (
*_load.sql) - При необходимости пишет DQ-проверки (
*_dq.sql) - Добавляет таски в существующий DAG
- Проверяет результат через DQ и запросы в Greenplum
Что студент НЕ делает
- Не поднимает инфраструктуру с нуля (Docker Compose дан)
- Не пишет DAG с нуля (шаблон дан)
- Не настраивает Airflow Connections (преднастроены)
- Не работает с PXF-конфигурацией (настроен)
Выбор эталонного среза
Эталонный срез: витрина 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 |
| STG | bookings, tickets, segments, flights, boarding_passes, airports |
Задание студенту:
| Слой | Таблицы | Что нового для студента |
|---|---|---|
| STG | airplanes, seats, routes |
Практика по аналогии с эталоном |
| ODS | airplanes, seats, routes |
Практика SCD1 UPSERT по аналогии |
| DDS | dim_airplanes (SCD1), dim_passengers (SCD1), dim_routes (SCD2) |
SCD2 — ключевой вызов курсовой |
| DM | airport_traffic, monthly_overview, route_performance, passenger_loyalty |
Разная сложность (от простой к сложной) |
Рекомендуемый порядок выполнения для студента:
- STG (airplanes, seats, routes) — разминка, по аналогии
- ODS (airplanes, seats, routes) — закрепление UPSERT
- DDS dim_airplanes, dim_passengers (SCD1) — новые измерения
- DDS dim_routes (SCD2) — ключевой вызов
- DM airport_traffic — простая витрина, похожа на sales_report
- DM route_performance — TRUNCATE+INSERT, SCD2-агрегация по BK
- DM monthly_overview — двухуровневая агрегация
- DM passenger_loyalty — самая сложная, пересчёт истории
SCD2 в задании: подход «рецепт без готового SQL»
Реализация dim_routes (SCD2) — ключевой вызов курсовой. Студент делает это
самостоятельно, но ТЗ содержит пошаговую подсказку:
- Алгоритм SCD2 текстом (без SQL):
- Вычисли
hashdiffпо набору атрибутов (атрибуты перечислены в ТЗ) - Найди строки, у которых
hashdiffизменился - Закрой старую версию (
valid_to = текущая_дата) - Вставь новую версию (
valid_from = текущая_дата,valid_to = NULL)
- Вычисли
- Формула hashdiff:
md5(concat_ws('|', field1, field2, ...)) - Ссылка на
naming_conventions.md(поляvalid_from,valid_to,hashdiff) - Напоминание: полуоткрытый интервал
[valid_from, valid_to) - Если застрял — ветка
solution
Самостоятельная реализация — ключ к запоминанию. SCD2 — обязательный вопрос на собеседованиях DE, и студент должен уметь объяснить его на основе собственного опыта.
6.1. Валидационный DAG (bookings_validate)
Отдельный DAG для самопроверки студента. Запускается вручную в Airflow UI после реализации заданий. Таски сгруппированы по слоям — студент видит, где именно проблема.
Примерная структура тасков:
bookings_validate
├── validate_stg
│ ├── check_stg_airplanes_exists (таблица создана, >0 строк)
│ ├── check_stg_seats_exists
│ └── check_stg_routes_exists
├── validate_ods
│ ├── check_ods_airplanes_rowcount (ODS >= STG по кол-ву уникальных BK)
│ ├── check_ods_seats_rowcount
│ ├── check_ods_routes_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-витрины содержат данные за загруженные дни
7. Формат ТЗ от аналитика
Файл: docs/assignment/analyst_spec.md (или несколько файлов по слоям).
Для каждой таблицы-задания документ содержит:
- Имя таблицы и целевая схема (stg / ods / dds / dm)
- Описание — что хранит таблица, бизнес-смысл
- Список полей с типами и описанием
- Маппинг источников — откуда берётся каждое поле
- Бизнес-правила и фильтры (если есть)
- Тип историзации (SCD1 / SCD2 / snapshot / append)
- Гранулярность (одна строка = ?)
- Distribution key (подсказка или задание на выбор)
Формат — приближен к реальным ТЗ, которые студент встретит на работе.
8. Ветки и workflow
main (стартовое состояние)
├── Эталонный срез: реализованные таблицы + DAG
├── ТЗ от аналитика
├── Инфраструктура (Docker, Make, PXF)
├── Заглушки / TODO-маркеры для студенческих заданий
└── DQ-проверки для самоконтроля
solution (полное решение)
└── Все таблицы реализованы — эталон для самопроверки
и подсказка, если студент застрял
Workflow студента
- Форкает репозиторий
- Читает README и ТЗ
make up— поднимает стендmake bookings-init— инициализирует источник- Запускает DDL-DAG'и (эталонные таблицы создаются)
- Запускает ETL-DAG'и — эталонный срез работает
- Реализует задания из ТЗ (SQL + таски в DAG)
- Проверяет себя через DQ
make bookings-generate-day— генерирует новый день, проверяет инкрементальность- Защищает работу перед ментором
9. Критерии приёмки курсовой
Для студента (самопроверка)
- Стенд поднимается (
make up) без ошибок - Все DAG'и проходят без failed-тасков
- Данные доезжают от STG до DM
- DQ-проверки проходят на всех реализованных таблицах
- После
make bookings-generate-day+ повторного запуска DAG данные корректно доливаются (инкрементальность работает)
Для ментора (ревью + защита)
- Код соответствует naming conventions (
docs/internal/naming_conventions.md) - SQL идемпотентен (повторный запуск не ломает данные)
- Distribution keys выбраны осмысленно
- Студент может объяснить: почему delete+insert, а не MERGE; разницу SCD1/SCD2; что такое HWM; как работает batch_id
- Код оформлен для портфолио (чистый Git-history, README)
10. Ограничения и риски
| Риск / ограничение | Митигация |
|---|---|
| Стенд тяжёлый (~8-16 GB RAM) | Указать минимальные требования; не утяжелять |
| bookings-db генерирует данные медленно | Не добавлять нагрузку; задокументировать ожидание |
| Студент может застрять надолго | Ветка solution как подсказка; еженедельные встречи |
| Greenplum 6.x — устаревающая версия | Для учебных целей достаточно; паттерны переносимы |
| PXF нестабилен при холодном старте | Задокументировано в README; healthcheck настроен |
Требования к машине студента
- 2-4 CPU, 8-16 GB RAM, 25-40 GB диска
- Linux / WSL2 / macOS
- Docker + Docker Compose
11. Таймлайн
| Когда | Что |
|---|---|
| Ближайшие 2-3 нед. | Первый студент может подойти к курсовой |
| До этого момента | Подготовить: ТЗ, стартовое состояние main, ветку solution |
12. Открытые вопросы (TODO)
-
Выбор эталонного среза— РЕШЕНО. Эталон:sales_reportи её цепочка. Задание: остальные 4 витрины + 3 STG/ODS + 3 DDS-измерения. SCD2 (dim_routes) — задание с подсказками в ТЗ. (Раздел 6) -
Формат DQ для самоконтроля— РЕШЕНО. Отдельный валидационный DAG (bookings_validate.py). Под капотом — SQL-проверки. Студент запускает DAG в Airflow UI и видит красные/зелёные таски по слоям. Дополнительный бонус — практика чтения логов Airflow. (см. Раздел 6.1 ниже) -
Вынос CSV-пайплайна — перенести
csv_to_greenplum.py,csv_to_greenplum_dq.py,ddl_greenplum_base.py,helpers/greenplum.py,sql/base/orders_ddl.sqlи связанные тесты в репозиторий airflow-manual. Решение принято, нужно выполнить. -
Подготовка main — какие изменения внести в main для стартового состояния (убрать лишние реализации, добавить TODO-маркеры)?
-
Название — рабочее: «Greenplum Bookings DWH». Финализировать.