Merge branch 'docs/main-docs'

This commit is contained in:
2026-01-09 19:46:54 +03:00
11 changed files with 340 additions and 530 deletions
+1 -1
View File
@@ -66,7 +66,7 @@
## Безопасность и конфигурация
- Все настройки — через `.env`; креды в коде не хардкодим. Частые переменные: `GP_*`, `PG_*`, `AIRFLOW_*`, `CSV_*`.
- `make down` удаляет тома — предупреждайте студентов, что данные пропадут.
- `make clean` удаляет тома — предупреждайте студентов, что данные пропадут.
## Для агента (особенности аудитории)
- Пишите простыми словами. Добавляйте короткие комментарии к нетривиальной логике.
+87 -440
View File
@@ -1,493 +1,140 @@
# airflow-dwh-gp-lab
# airflow-dwh-gp-lab
Учебный стенд: ETL из Postgres в Greenplum с оркестрацией в Airflow.
Учебный стенд для лабораторных по Data Engineering: **Airflow** оркестрирует загрузку данных из демо‑БД
**bookings** (Postgres) в **Greenplum**.
Добро пожаловать в учебный стенд для изучения основ Data Engineering! Этот проект поможет вам освоить ключевые инструменты современных data pipeline: **Airflow** для оркестрации, **pandas/CSV** для подготовки данных, **Postgres** с демобазой **bookings** как источник и **Greenplum** как аналитическую базу данных.
## Зачем этот стенд
Если вы проходите стенд как серию лабораторных, смотрите также файл с заданиями: `educational-tasks.md`.
Основная цель стенда — дать вам удобное место для лабораторных работ и будущей курсовой:
вы построите небольшое аналитическое хранилище данных (DWH) на базе **Greenplum** и закрепите навыки:
## 🎯 Что вы узнаете
- моделирования данных (слои DWH, модели, витрины);
- построения ETL/ELT;
- работы с Airflow и Greenplum.
- Как настроить локальный стек данных с помощью Docker
- Как Airflow управляет workflow и координирует задачи
- Как генерировать датасеты через pandas и сохранять их в CSV
- Как загружать данные в Greenplum пакетами и избегать дублей
- Как проверять качество данных в автоматизированных pipeline
- Основы проектирования ETL/ELT процессов
- Как работать с демо-БД bookings в Postgres как источником для будущего DWH
В курсовой у нас один источник данных — демо‑БД **bookings**. В стенде уже есть готовый учебный пример
загрузки **bookings → stg в Greenplum**, чтобы вы могли сфокусироваться на DWH‑части (ODS/DDS/DM) и
не тратить время на инфраструктуру.
## 👩‍🎓 Для студентов (10‑минутный чек‑лист)
## Что внутри
- Установите Docker Desktop и Git.
- Скопируйте настройки: `cp .env.example .env`.
- Поднимите стенд: `make up` (или `docker compose up -d` — сервис `airflow-init` запустится автоматически при первом старте).
- Откройте UI: http://localhost:8080 (admin/admin).
- Включите и запустите DAG `csv_to_greenplum`. Дождитесь Success.
- Проверьте данные: `make gp-psql``SELECT COUNT(*) FROM public.orders;`.
- Дополнительно: запустите `csv_to_greenplum_dq` — все проверки должны быть зелёные.
- **Airflow** (UI: http://localhost:8080, логин/пароль: admin/admin)
- **Greenplum** (singlenode для обучения; внешний порт по умолчанию `5435`)
- **bookings-db** (Postgres с демо‑БД `demo`; внешний порт по умолчанию `5434`)
- **PXF** как “транспорт” между Postgres и Greenplum (уже настроен в образе)
- Побочный пример: загрузка данных через **pandas/CSV** (`csv_to_greenplum`)
Если что‑то не работает — смотрите «Типичные проблемы» и «Быстрый reset» ниже.
## PXF (в 5 строк)
## 🚀 Быстрый старт (для новичков)
PXF (Platform Extension Framework) — компонент Greenplum для работы с внешними источниками.
В этом стенде PXF используется для чтения таблицы `bookings.bookings` из Postgres прямо из Greenplum
через внешнюю таблицу `stg.bookings_ext`. Поэтому загрузка в `stg.bookings` выглядит как обычный
`INSERT ... SELECT` без промежуточных CSV.
Подробное руководство по PXF смотрите в официальной документации Greenplum.
### Шаг 1: Подготовка окружения
## Быстрый старт (основной сценарий: bookings → stg)
**Требования:**
- Docker Desktop (Windows/Mac) или Docker Engine 24+ (Linux)
- Git для клонирования репозитория
> 💡 **Совет:** Если у вас Windows, рекомендуем использовать WSL (Windows Subsystem for Linux) для лучшей совместимости.
### Шаг 2: Настройка проекта
1) Скопируйте настройки:
```bash
# Скопируйте файл настроек
cp .env.example .env
# Запустите стек (это может занять 2-3 минуты при первом запуске)
docker compose up -d
```
### Шаг 3: Первый запуск pipeline
1. Откройте Airflow UI: **http://localhost:8080** (логин/пароль: admin/admin)
2. Найдите DAG с названием **csv_to_greenplum**
3. Нажмите на переключатель слева от названия DAG, чтобы включить его
4. Нажмите кнопку **Trigger** (значок воспроизведения ▶️)
🎉 **Поздравляем!** Вы только что запустили свой первый data pipeline:
- Система сгенерировала 1000 тестовых заказов при помощи pandas
- Датасет сохранился в CSV-файл в каталоге `./data`
- Airflow загрузил данные из CSV в Greenplum без дублей по `order_id`
### Шаг 4: Проверка результатов
**Проверка вручную:**
```bash
# Подключитесь к Greenplum и проверьте данные
docker compose exec greenplum bash -c "su - gpadmin -c 'psql -p 5432 -d gp_dwh'"
# Внутри psql выполните:
\dt # Показать таблицы
SELECT count(*) FROM public.orders; # Посчитать записи
# Посмотреть несколько строк
SELECT * FROM public.orders LIMIT 5;
```
CSV-файлы после выполнения DAG остаются в директории `./data`. Их можно открыть любым редактором или изучить через pandas.
### Быстрый reset
Если после изменений что‑то «сломалось»:
2) Поднимите стенд:
```bash
make down # Остановить и удалить контейнеры/сети (volumes сохраняются)
make up
# если make не установлен: docker compose up -d
```
Если проблема связана с «грязной» остановкой и данными в томах (например, Greenplum не стартует),
используйте полный reset: `make clean && make up` (данные в Docker-томах будут потеряны).
---
## 🛠️ Подробная настройка (для уверенных пользователей)
> Если вы впервые запускаете стенд, этот раздел можно пролистать и вернуться к нему позже.
### Установка Make (опционально)
Для удобства работы с проектом рекомендуем установить `make`:
- **Linux (Debian/Ubuntu):** `sudo apt install -y make`
- **macOS:** `brew install make`
- **Windows:**
- WSL: `sudo apt install -y make`
- Chocolatey: `choco install make`
- Scoop: `scoop install make`
С `make` команды становятся короче:
```bash
make up # Запуск стека (включая airflow-init при первом старте)
make logs # Просмотр логов
make gp-psql # Подключение к Greenplum
```
### Настройка подключения к Greenplum в Airflow
По умолчанию готовые DAG используют Airflow Connections. В docker-compose они
заводятся автоматически через переменные окружения:
- `AIRFLOW_CONN_GREENPLUM_CONN` — подключение к Greenplum с `conn_id=greenplum_conn`;
- `AIRFLOW_CONN_BOOKINGS_DB` — подключение к демо-БД bookings с `conn_id=bookings_db`.
Такие подключения подхватываются из окружения и могут не отображаться в UI,
но для DAG это нормально — `PostgresOperator` найдёт их по `conn_id`.
При желании вы можете создать или отредактировать подключение вручную в UI:
1. Airflow UI → **Admin → Connections → Add a new record**
2. Заполните поля:
- **Conn Id:** `greenplum_conn`
- **Conn Type:** `Postgres`
- **Host:** `greenplum`
- **Schema:** `gp_dwh`
- **Login:** `gpadmin`
- **Password:** `gpadmin`
- **Port:** `5432`
---
### Greenplum + PXF: свой образ
Чтобы избежать проблем с правами и нестабильных запусков, Greenplum собирается
из собственного `Dockerfile.greenplum`. В образ вшиты:
- JDBC-драйвер PostgreSQL;
- конфигурация PXF-сервера `bookings-db`;
- ensure-скрипт, который при каждом старте контейнера докладывает файлы в `PXF_BASE`.
Дополнительно при старте контейнера:
- базовые конфиги PXF копируются в `PXF_BASE/conf` (если их ещё нет);
- создаются каталоги `PXF_BASE/run` и `PXF_BASE/logs`;
- `CREATE EXTENSION pxf` выполняется автоматически, когда Greenplum становится доступен (с ретраями).
Healthcheck сервиса `greenplum` учитывает не только готовность Greenplum, но и запуск PXF,
а также наличие `extension pxf` — это нужно, чтобы Airflow не стартовал раньше PXF.
Сборка и запуск:
- `make build` — собрать образ (явно);
- `make up` — поднимет стек и соберёт образ, если он ещё не создан.
Обновление PXF-конфигов:
- изменили файлы в `pxf/` → выполните `make build` и перезапустите контейнер;
- для принудительной перезаписи файлов в `PXF_BASE` используйте `PXF_SEED_OVERWRITE=1`;
- для принудительного `pxf cluster sync` при старте используйте `PXF_SYNC_ON_START=1`.
Проверка PXF:
- статус: `docker compose exec greenplum bash -lc "su - gpadmin -c '/usr/local/pxf/bin/pxf cluster status'"`;
- логи: `greenplum_data:/data/pxf/logs` (внутри контейнера — `/data/pxf/logs`).
---
### Airflow: свой образ
Airflow тоже собирается из собственного `Dockerfile.airflow`, чтобы зависимости ставились при сборке, а не во время старта контейнеров. В образ включены:
- Python‑зависимости из `airflow/requirements.txt`;
- утилита `psql` для быстрых проверок внутри контейнера.
Если меняли `airflow/requirements.txt` или `Dockerfile.airflow`, пересоберите образы: `make build`, затем `make up`.
В docker-compose по умолчанию задан `AIRFLOW__CORE__EXECUTOR=LocalExecutor` (параллельное выполнение задач). Если нужен последовательный режим — замените на `SequentialExecutor` в `docker-compose.yml`.
---
### Локальное окружение разработчика
Локальным окружением управляет [uv](https://docs.astral.sh/uv/) — он скачивает нужный Python и создаёт `.venv` на основе `pyproject.toml` / `uv.lock`.
3) Инициализируйте демо‑БД bookings:
```bash
uv sync
make bookings-init
```
`uv sync` сам подтянет версию Python из `.python-version`/`pyproject.toml`, создаст `.venv` и установит зависимости. Для тех же действий можно использовать `make dev-sync`. Цель `make dev-setup` (или вручную `uv python install` + `uv python pin`) нужна только когда вы меняете версию Python или прогреваете кэш.
4) Подготовьте STG‑объекты в Greenplum (выберите один вариант):
> Если требуется «классическое» активированное окружение, после `uv sync` выполните `.\.venv\Scripts\Activate.ps1` в PowerShell или `source .venv/bin/activate` в Unix-терминале.
- Учебный вариант: в Airflow UI запустите DAG `bookings_stg_ddl`;
- Технический шорткат: `make ddl-gp` (применяет все DDL разом вручную).
Проверки и форматирование выполняем через uv:
5) Запустите основной DAG `bookings_to_gp_stage`.
6) Проверьте результат в Greenplum:
```bash
make test # uv run pytest -q
make lint # black/isort в режиме проверки
make fmt # автоформатирование black + isort
make gp-psql
-- внутри psql:
SELECT COUNT(*) FROM stg.bookings;
SELECT * FROM stg.bookings ORDER BY src_created_at_ts DESC LIMIT 10;
```
#### Быстрый старт с uv
Подробнее про логику DAG и проверки — `docs/bookings_to_gp_stage.md`.
## DAG-и в стенде
Основные (для потока bookings → DWH):
- `bookings_stg_ddl` — создаёт `stg.bookings_ext` и `stg.bookings` в Greenplum;
- `bookings_to_gp_stage` — генерирует учебный день в `bookings-db` и грузит инкремент в `stg.bookings`
(через PXF), затем выполняет DQ‑проверку.
Вспомогательные (побочный трек с CSV):
- `orders_base_ddl` — создаёт таблицу `public.orders` для CSV‑пайплайна;
- `csv_to_greenplum` — pandas → CSV → Greenplum (пример загрузки без источника‑БД);
- `csv_to_greenplum_dq` — проверки качества данных для `public.orders`.
## Полезные команды
```bash
uv sync
uv run pytest -q
uv run black --check airflow tests
make up # поднять стек
make logs # логи airflow-webserver и airflow-scheduler
make gp-psql # psql в Greenplum
make bookings-psql # psql в демо-БД bookings (Postgres)
make ddl-gp # применить DDL к Greenplum вручную (вместо DDL-DAG)
make down # остановить и удалить контейнеры/сети (volumes сохраняются)
make clean # полный reset: удалить контейнеры/сети и volumes (данные будут потеряны)
```
> Не устанавливайте пакеты напрямую через `pip install --user ...`. Если что-то уже попало в user-site, удалите `pip uninstall <package>` и проверьте `pip list --user`.
---
## Подключение через DBeaver (опционально)
## 📋 Что входит в стенд
### Основные компоненты
- **Greenplum** — аналитическая база данных для хранения и анализа данных
- **Airflow** — оркестратор workflow и задач
- **Postgres** — база метаданных для Airflow
- **Postgres (bookings)** — отдельная демо-БД bookings (источник данных для будущего DWH в Greenplum)
- **pandas** — библиотека для генерации и анализа данных в формате CSV
### Готовые DAG (workflow)
- **orders_base_ddl** — создаёт базовую таблицу `public.orders` для CSV‑пайплайна
- **bookings_stg_ddl** — готовит схему `stg` и таблицы `stg.bookings_ext` / `stg.bookings`
- **csv_to_greenplum** — базовый pipeline: pandas → CSV → Greenplum
- **bookings_to_gp_stage** — пример загрузки из демо‑БД bookings в слой STG
- **csv_to_greenplum_dq** — проверки качества данных (наличие таблицы, схема, дубликаты)
> Учебный путь — триггернуть DDL‑DAG: для CSV `orders_base_ddl`, для bookings `bookings_stg_ddl`. Технический шорткат для быстрой инициализации — `make ddl-gp` (он не вызывается автоматически при старте контейнеров).
### Полезные команды
```bash
# Основные команды
make up # Запустить весь стенд (Airflow инициализируется автоматически при первом старте)
make stop # Остановить контейнеры, не трогая данные
make down # Остановить и удалить контейнеры/сети (volumes сохраняются)
make clean # Полный reset: остановить и удалить контейнеры/сети и тома (данные будут потеряны)
make airflow-init # Ручной запуск инициализации Airflow (обычно не нужен)
make ddl-gp # Применить DDL к Greenplum вручную
make gp-psql # Подключиться к Greenplum через psql
make bookings-init # Установить демобазу bookings в Postgres (по умолчанию генерирует 1 день)
make bookings-generate-day # Добавить ещё один день данных в bookings (можно вызвать несколько раз)
make bookings-psql # Подключиться к демобазе bookings (БД demo)
# Проверка данных
make logs # Следить за логами Airflow
# Логи задач Airflow сохраняются в Docker-томе `airflow_logs`
# и переживают `docker compose down`/`up` (удаляются при `docker compose down -v` / `make clean`).
# Контроль генерации bookings
docker compose -f docker-compose.yml exec bookings-db bash -lc 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d demo -c "SELECT busy();"'
# busy() = t — генерация ещё идёт; f — завершена. При необходимости можно вызвать CALL abort(); и запустить генерацию заново.
```
### Генерация следующего дня в bookings
- При `make bookings-init` автоматически генерируется `BOOKINGS_INIT_DAYS` суток, начиная с даты `BOOKINGS_START_DATE` (по умолчанию один день с `2017-01-01`).
- Дальше каждый вызов `make bookings-generate-day` или генерации через DAG добавляет ровно **один** следующий день после `max(book_date)` в `bookings.bookings` — генератор сам смотрит последнюю дату.
- Рекомендуемый учебный сценарий для DAG `bookings_to_gp_stage`: запускать DAG по одному дню вперёд, выбирая в форме Trigger логическую дату `Execution Date (ds)`, совпадающую с тем днём, который вы хотите загрузить (например, `2017-01-01`, затем `2017-01-02` и т.д.).
- Быстрее всего: `make bookings-generate-day` — читает GUC и сам вызывает `continue`.
- Вручную из psql/DBeaver (подзапросы в аргументах CALL не работают, поэтому через DO-блок):
```sql
DO $$
DECLARE
v_next_day timestamptz;
BEGIN
SELECT date_trunc('day', max(book_date)) + interval '1 day'
INTO v_next_day
FROM bookings.bookings;
CALL continue(v_next_day); -- или CALL continue(v_next_day, 4) для параллельности
END $$;
```
- Не вызывайте `CALL generate(...)` поверх существующих данных: она делает TRUNCATE и создаёт демобазу заново.
---
## ⚙️ Настройка через переменные окружения
Все настройки находятся в файле `.env`. Основные параметры:
Перед подключением убедитесь, что стенд поднят (`make up`).
### Greenplum
- `GP_USER` — пользователь (по умолчанию: gpadmin)
- `GP_PASSWORD` — пароль (по умолчанию: gpadmin)
- `GP_DB` — база данных (по умолчанию: gp_dwh)
- `GP_PORT` — порт Greenplum внутри Docker-сети (по умолчанию: 5432, менять обычно не нужно; внешний порт на хосте для подключения клиентов — 5435).
### Демо-БД bookings (Postgres)
- `BOOKINGS_DB_USER` — пользователь Postgres для демобазы (по умолчанию: bookings)
- `BOOKINGS_DB_PASSWORD` — пароль пользователя (по умолчанию: bookings)
- `BOOKINGS_DB_NAME` — база данных, из которой запускается установка генератора (по умолчанию: bookings)
- `BOOKINGS_DB_PORT` — внешний порт для подключения к контейнеру bookings-db (по умолчанию: 5434)
- `BOOKINGS_START_DATE` — начальная дата модельного времени (по умолчанию: 2017-01-01)
- `BOOKINGS_INIT_DAYS` — сколько дней сгенерировать при первой инициализации (по умолчанию: 1, чтобы увидеть данные без долгого ожидания; можно менять при вызове `BOOKINGS_INIT_DAYS=... make bookings-init`)
- `BOOKINGS_JOBS` — число параллельных джобов генератора bookings (по умолчанию: 1; при 1 генерация идёт синхронно без dblink)
- `Host`: `localhost`
- `Port`: `5435`
- `Database`: значение `GP_DB` из `.env` (по умолчанию `gp_dwh`)
- `Username`: значение `GP_USER` (по умолчанию `gpadmin`)
- `Password`: значение `GP_PASSWORD` (по умолчанию `gpadmin`)
### CSV pipeline
- `CSV_DIR` — путь к каталогу с CSV внутри контейнеров Airflow (по умолчанию: `/opt/airflow/data`)
- `CSV_ROWS` — количество строк, генерируемых DAG (по умолчанию: 1000)
### bookings-db (Postgres, демо-БД `demo`)
### Airflow
- `GP_CONN_ID` — ID подключения (по умолчанию: greenplum_conn)
- `Host`: `localhost`
- `Port`: значение `BOOKINGS_DB_PORT` из `.env` (по умолчанию `5434`)
- `Database`: `demo`
- `Username`: значение `BOOKINGS_DB_USER` (по умолчанию `bookings`)
- `Password`: значение `BOOKINGS_DB_PASSWORD` (по умолчанию `bookings`)
---
## Документация
## 🔍 Продвинутые темы
- Учебные задания: `educational-tasks.md`).
- План тестирования/проверок и негативные кейсы: `TESTING.md`.
- Дополнительные заметки и технические детали: `docs/README.md`.
> Этот раздел не обязателен при первом прохождении стенда; к нему удобно вернуться, когда базовый CSV‑pipeline уже понятен.
### Архитектура pipeline
**Поток данных в DAG `csv_to_greenplum`:**
1. `create_orders_table` — создаёт таблицу `public.orders` в Greenplum
2. `generate_csv` — генерирует датасет при помощи pandas и сохраняет CSV в `CSV_DIR`
3. `preview_csv` — выводит предпросмотр и статистику по данным
4. `load_csv_to_greenplum` — загружает CSV во временную таблицу и переносит новые строки в `public.orders`
> 💡 **Безопасность повторного запуска:** Pipeline защищен от дубликатов, поэтому его можно запускать многократно.
### Проверка качества данных
Запустите DAG `csv_to_greenplum_dq` для автоматической проверки:
- Наличие таблицы в базе
- Соответствие схемы ожидаемой структуре
- Объем загруженных данных
- Отсутствие дубликатов записей
### Пример DAG с SQL-скриптами (bookings → stg)
> Если вы ещё не дошли до части про bookings и слои DWH, этот подраздел можно пропустить на первом чтении.
В репозитории есть учебный DAG `bookings_to_gp_stage`, который показывает «канонический» способ работы с SQL в Airflow:
- подключение к БД через Airflow Connections (`bookings_db`, `greenplum_conn`);
- бизнес-логика инкрементальной загрузки и DQ вынесена в SQL-файлы в каталоге `sql/`:
- `sql/src/bookings_generate_day_if_missing.sql` — генерация следующего учебного дня в демо-БД bookings (или нескольких стартовых дней, если база пуста);
- `sql/stg/bookings_ddl.sql` — DDL для схемы `stg` и таблиц `stg.bookings_ext` / `stg.bookings`;
- `sql/stg/bookings_load.sql` — загрузка инкремента из `stg.bookings_ext` в `stg.bookings` на основе «хвоста» после предыдущих батчей;
- `sql/stg/bookings_dq.sql` — проверка количества строк между источником и stg за то же окно.
Фрагмент DAG:
```python
load_bookings_to_stg = PostgresOperator(
task_id="load_bookings_to_stg",
postgres_conn_id="greenplum_conn",
sql="stg/bookings_load.sql",
params={"batch_id": "{{ run_id }}"},
)
```
Такой подход помогает держать оркестрацию (DAG) и SQL-логику в отдельных файлах и легче сравнивать её с теорией из статьи про моделирование DWH.
### Ограничения учебного стенда
- **Greenplum** запущен в single-node режиме (для обучения)
- В продакшене Greenplum обычно разворачивают кластером на нескольких серверах
- Используется Greenplum 6 (широко доступная версия), хотя Greenplum 7 предлагает больше возможностей
---
## 🧩 Подключение к базам через DBeaver
> Необязательный раздел: нужен только если вы хотите смотреть данные через DBeaver. Для базовых заданий достаточно `make gp-psql`.
Ниже — краткая инструкция, как подключиться к Greenplum и демо-БД bookings из DBeaver. Перед этим убедитесь, что стенд запущен:
- `cp .env.example .env` (если ещё не делали)
- `make up`
- `make bookings-init`
- `make ddl-gp`
### Greenplum (аналитическая БД)
1. Откройте DBeaver → **New Database Connection**.
2. Выберите драйвер **PostgreSQL** (или **Greenplum**, если он есть в вашей версии DBeaver).
3. На вкладке **Main** заполните поля (по умолчанию):
- `Host`: `localhost`
- `Port`: `5435` (внешний порт Greenplum на хосте)
- `Database`: значение `GP_DB` (по умолчанию `gp_dwh`)
- `Username`: значение `GP_USER` (по умолчанию `gpadmin`)
- `Password`: значение `GP_PASSWORD` (по умолчанию `gpadmin`)
4. Нажмите **Test Connection** → **OK**, затем **Finish**.
После подключения:
- Основные таблицы лаба — в схеме `public` базы `gp_dwh` (например, `public.orders`).
- После настройки PXF и выполнения `make ddl-gp` станет доступна внешняя таблица `public.ext_bookings_bookings` — чтение из демо-БД bookings через PXF.
### bookings-db (демо-БД источника)
Для работы с исходными данными (демо-БД `demo`) достаточно стандартного PostgreSQL-подключения.
1. Откройте DBeaver → **New Database Connection** → драйвер **PostgreSQL**.
2. На вкладке **Main** укажите (значения по умолчанию из `.env.example`):
- `Host`: `localhost`
- `Port`: значение `BOOKINGS_DB_PORT` из `.env` (по умолчанию `5434`)
- `Database`: `demo`
- `Username`: `BOOKINGS_DB_USER` (по умолчанию `bookings`)
- `Password`: `BOOKINGS_DB_PASSWORD` (по умолчанию `bookings`)
3. Нажмите **Test Connection** → **OK**, затем **Finish**.
После подключения:
- Основные таблицы находятся в схеме `bookings` базы `demo` (например, `bookings.bookings`, `bookings.tickets`, `bookings.flights` и т.д.).
- Можно сравнивать данные:
- между `bookings.bookings` в Postgres и `public.ext_bookings_bookings` в Greenplum;
- между временем (`book_date`) в UTC в `demo` и локальным временем в Greenplum (учитывая `TZ=Europe/Moscow`).
> Если вы меняли порты или креды в `.env`, не забудьте подставить те же значения в настройках соединений в DBeaver.
---
## 🆘 Типичные проблемы и решения
## Типичные проблемы и решения
| Проблема | Решение |
|----------|---------|
| Airflow UI не открывается | Дождитесь сообщения `Listening at: http://0.0.0.0:8080` в логах (`make logs`) |
| `database "demo" does not exist` в bookingsDAG | Вы сделали полный reset с удалением томов (`docker compose down -v` / `make clean`), поэтому демобаза bookings не установлена. Запустите `make bookings-init` и повторите DAG. |
| Ошибка подключения к Greenplum | Убедитесь, что контейнер `greenplum` стал статусом `healthy` (проверьте `docker compose ps`) |
| Не открывается порт 8080/5433/5434/5435 | Проверьте, что эти порты не заняты локальными сервисами; при необходимости остановите их или измените порты в `.env`/`docker-compose.yml` |
| Нет файла в `./data` после запуска DAG | Проверьте логи задачи `generate_csv`, убедитесь, что `CSV_DIR` смонтирован в docker-compose |
| Команда `make` не найдена | Используйте полные команды `docker compose` или установите make |
| Greenplum не стартует/падает при старте | Попробуйте `make down && make up`. Если не помогло — полный reset: `make clean && make up` (удалит тома). |
| `database "demo" does not exist` в bookingsDAG | Вы сделали reset с удалением volumes (`make clean` / `docker compose down -v`). Запустите `make bookings-init` и повторите DAG. |
| Ошибка подключения к Greenplum | Убедитесь, что контейнер `greenplum` имеет статус `healthy` (`docker compose ps`) |
| `protocol "pxf" does not exist` | Перезапустите `greenplum` и повторите `bookings_stg_ddl`/`make ddl-gp` — расширение `pxf` создаётся автоматически при старте контейнера. |
| PXF не отвечает (Connection refused к порту 5888) | Проверьте `pxf cluster status` в контейнере `greenplum` и перезапустите сервис `greenplum`. |
| PXF не подхватывает изменения конфигов | Пересоберите образ (`make build`) и перезапустите `greenplum`. Для принудительной перезаписи файлов задайте `PXF_SEED_OVERWRITE=1`. |
| DAG `bookings_to_gp_stage` падает на внешней таблице/подключении к bookings | Убедитесь, что запущен контейнер `bookings-db` (`docker compose ps`, при необходимости `docker compose start bookings-db`), и выполнены `make bookings-init` и `make ddl-gp` или DAG `bookings_stg_ddl` |
| DAG `bookings_to_gp_stage` ругается на отсутствующие таблицы stg | Запустите DAG `bookings_stg_ddl` (или выполните `make ddl-gp`), затем повторите запуск |
| DAG не видит Greenplum/DEMObase по Airflow Connections | Убедитесь, что контейнеры `greenplum` и `bookings-db` запущены (`docker compose ps`). Подключения `greenplum_conn` и `bookings_db` задаются через переменные окружения `AIRFLOW_CONN_...` и могут не отображаться в UI, но `PostgresOperator` всё равно найдёт их по `conn_id`. При необходимости вы можете создать/отредактировать их вручную в разделе Connections. |
---
## 📁 Структура проекта
```
├── docker-compose.yml # Описание всех сервисов
├── Dockerfile.airflow # Образ Airflow с зависимостями
├── Dockerfile.greenplum # Образ Greenplum с интегрированным PXF
├── .env.example # Шаблон настроек
├── Makefile # Удобные команды для работы
├── README.md # Обзор стенда
├── TESTING.md # Пошаговый план проверки
├── educational-tasks.md # Учебные задания для менти
├── airflow/
│ └── dags/ # Файлы workflow (DAG)
│ ├── csv_to_greenplum.py
│ ├── csv_to_greenplum_dq.py
│ └── bookings_to_gp_stage.py
├── bookings/ # Скрипты и файлы для демобазы bookings в Postgres
├── sql/
│ └── ddl_gp.sql # Общий DDL для Greenplum (подключает stg/src-скрипты)
├── docs/ # Дополнительные документы (архитектура, bookings/STG, PXF)
├── tests/ # Автоматические тесты (pytest)
└── pxf/ # Конфигурация и файлы для PXF
```
---
## 💡 Советы для дальнейшего обучения
1. **Поэкспериментируйте с DAG** — измените параметры генерации данных или размер батча
2. **Добавьте свои проверки** — расширьте DAG `csv_to_greenplum_dq.py`
3. **Попробуйте другие источники** — замените генератор данных на чтение из файла или API
4. **Изучите Airflow deeper** — добавьте зависимости между задачами, настройте расписания
---
## ✅ Тестирование
- Локальные проверки: `make test` (pytest). Для форматирования — `make fmt`, для проверки — `make lint`.
- Пошаговый сценарий с Docker (включая негативные кейсы и reset) — см. `TESTING.md`.
- Полный smoke-тест стенда (сносит volumes!): `make e2e-smoke` — поднимает стек с нуля, прогоняет `csv_to_greenplum` и `bookings_to_gp_stage` через `airflow dags test` и проверяет, что в `public.orders` и `stg.bookings` появились строки.
---
| DAG `bookings_to_gp_stage` ругается на отсутствующие таблицы stg | Запустите `bookings_stg_ddl` (или выполните `make ddl-gp`), затем повторите запуск |
| Порты 8080/5434/5435 заняты | Остановите локальные сервисы или измените порты в `.env`/`docker-compose.yml` |
## Благодарности
- **Postgres Pro** — за демо-БД bookings и генератор данных `demodb`: https://github.com/postgrespro/demodb (лицензия MIT: https://github.com/postgrespro/demodb/blob/main/LICENSE).
- **Postgres Pro** — за демоБД bookings и генератор данных `demodb`: https://github.com/postgrespro/demodb (лицензия MIT: https://github.com/postgrespro/demodb/blob/main/LICENSE).
- **woblerr** — за Docker-сборку Greenplum: https://github.com/woblerr/docker-greenplum (образ: `woblerr/greenplum`, лицензия MIT: https://github.com/woblerr/docker-greenplum/blob/master/LICENSE).
Удачи в изучении Data Engineering! 🚀
+3 -2
View File
@@ -80,8 +80,9 @@
- Хостовые CSV: каталог `data/` (можно открыть любой файл и убедиться в структуре).
## 9. Завершение работы
- `make down` — выключает сервисы и удаляет тома (перезапишет данные в Greenplum!).
- При необходимости сохранить данные: скопировать CSV из `data/` и дампы из контейнера до `make down`.
- `make down` — выключает сервисы и удаляет контейнеры/сети (volumes сохраняются).
- Полный сброс данных (удаляет volumes): `make clean`.
- При необходимости сохранить данные: скопировать CSV из `data/` и сделать дампы до `make clean`.
## Текущий статус (пример успешного прогона)
- `uv run pytest -q` — 11 passed, 2 smoke-теста DAG пропущены (Airflow не установлен в venv).
+17
View File
@@ -0,0 +1,17 @@
# Документация
Этот каталог содержит дополнительные материалы к учебному стенду.
## Быстрый путь (для менти)
- [Быстрый старт и команды](../README.md)
- [Учебные задания](../educational-tasks.md)
- [План тестирования и проверки](../TESTING.md)
- [Главный учебный DAG: bookings → stg](bookings_to_gp_stage.md)
## Технические детали (опционально)
- [Как устроен Docker-стенд (образы, Connections, переменные окружения)](stack.md)
- [PXF в этом проекте (проектная реализация)](internal/pxf_bookings.md)
- [Дизайн stg для bookings (черновик)](internal/bookings_stg_design.md)
- [Про время/UTC в bookings (черновик)](internal/bookings_tz.md)
+100
View File
@@ -0,0 +1,100 @@
# DAG `bookings_to_gp_stage`: `bookings-db``stg` в Greenplum
Этот DAG — основной учебный пример в стенде. Он показывает путь данных из источника **Postgres**
(`bookings-db`, демо‑БД `demo`) в сырой слой **STG** в **Greenplum** с инкрементальной загрузкой
и простой проверкой качества данных.
## Что делает DAG
- В источнике (`bookings-db`) генерирует следующий учебный день данных в `bookings.bookings`
(генератор всегда “шагает” вперёд от `max(book_date)`).
- В Greenplum загружает инкремент в `stg.bookings` через внешнюю таблицу `stg.bookings_ext` (PXF).
- Сверяет количество строк между источником (за окно инкремента) и загруженным батчем.
## Что должно быть готово перед запуском
1) Стек поднят:
```bash
make up
```
2) Демо‑БД bookings установлена и содержит данные:
```bash
make bookings-init
```
3) В Greenplum созданы `stg.bookings_ext` и `stg.bookings` (выберите один вариант):
- учебный вариант: запустить DAG `bookings_stg_ddl` в Airflow UI;
- технический шорткат: `make ddl-gp`.
4) Airflow Connections:
- `bookings_db` — подключение к источнику `bookings-db`;
- `greenplum_conn` — подключение к Greenplum.
По умолчанию они задаются через переменные окружения `AIRFLOW_CONN_...` в `docker-compose.yml`,
поэтому могут не отображаться в UI — для `PostgresOperator` это нормально.
## Как запустить
1) Откройте Airflow UI: http://localhost:8080 (admin/admin).
2) Запустите DAG `bookings_to_gp_stage` вручную (Trigger DAG).
3) Дождитесь статуса Success у всех задач.
## Как это работает внутри (по шагам)
1) `generate_bookings_day`
- выполняет `sql/src/bookings_generate_day_if_missing.sql` в `bookings-db`;
- если `bookings.bookings` пустая — вызывает `generate(...)` на `BOOKINGS_INIT_DAYS`;
- иначе — вызывает `continue(...)`, добавляя ровно один следующий день.
2) `load_bookings_to_stg`
- выполняет `sql/stg/bookings_load.sql` в Greenplum;
- берёт строки из `stg.bookings_ext`, которые попадают в новое окно инкремента;
- вставляет их в `stg.bookings`, добавляя тех.колонки:
- `src_created_at_ts` (опорная метка времени для инкремента),
- `load_dttm`,
- `batch_id={{ run_id }}`.
3) `check_row_counts`
- выполняет `sql/stg/bookings_dq.sql` в Greenplum;
- считает количество строк в источнике за то же окно инкремента и сравнивает с количеством строк,
вставленных в `stg.bookings` для текущего `batch_id`;
- при расхождении делает `RAISE EXCEPTION` с понятным текстом.
4) `finish_summary`
- логирует краткую сводку в конце запуска.
## Как проверить результат
```bash
make gp-psql
```
Примеры запросов:
```sql
SELECT COUNT(*) FROM stg.bookings;
SELECT
src_created_at_ts,
load_dttm,
batch_id
FROM stg.bookings
ORDER BY src_created_at_ts DESC
LIMIT 10;
```
## Типичные ошибки
- `database "demo" does not exist`: демо‑БД не установлена → выполните `make bookings-init`.
- Ошибки про `stg.bookings_ext`/`stg.bookings`: не применён DDL → запустите `bookings_stg_ddl` или `make ddl-gp`.
- Ошибки PXF (`protocol "pxf" does not exist`, connection refused): перезапустите `greenplum` и повторите DDL.
Для технических деталей см. `docs/internal/pxf_bookings.md`.
+2 -2
View File
@@ -68,7 +68,7 @@ DDL будет добавлен в `sql/ddl_gp.sql` в блоке DDL для Gre
- считаем режим `full` — загружаем все строки из `stg.bookings_ext`.
- При последующих запусках:
- читаем `max(src_created_at_ts)` из `stg.bookings` за все предыдущие загрузки;
- считаем, что нужно загрузить только строки, где `src_created_at_ts` больше этой максимальной метки и не позже конца текущего учебного дня.
- загружаем строки, где `src_created_at_ts` больше этой максимальной метки (верхняя граница по времени не задаётся).
Таким образом, вся логика инкремента «замкнута» на один техно‑столбец `src_created_at_ts`, который студент потом сможет использовать и на следующих слоях (например, в CDC‑логике).
@@ -88,7 +88,7 @@ DDL будет добавлен в `sql/ddl_gp.sql` в блоке DDL для Gre
- `dag_id`: `bookings_to_gp_stage`.
- Основные параметры:
- `batch_id` (по умолчанию `{{ ds_nodash }}`) — метка батча, которая попадает в `stg.bookings.batch_id`;
- `batch_id` (в текущей реализации `{{ run_id }}`) — метка батча, которая попадает в `stg.bookings.batch_id`;
- подключения:
- `bookings_db_conn_id` — Airflow connection к `bookings-db` (в коде DAG — `BOOKINGS_CONN_ID = "bookings_db"`);
- `greenplum_conn_id` — Airflow connection к Greenplum (`GREENPLUM_CONN_ID = "greenplum_conn"`).
-80
View File
@@ -1,80 +0,0 @@
# Мини‑README по учебному DAG bookings_to_gp_stage (черновик)
_Внутренний файл, чтобы не забыть договорённости. Перед итоговой сдачей документацию по блоку bookings/STG нужно будет аккуратно собрать и переписать._
## 1. Что делает DAG
- DAG `bookings_to_gp_stage` показывает учебный поток:
- источник: демо‑БД `bookings-db` (Postgres, схема `bookings`, таблица `bookings.bookings`);
- при каждом запуске генерируется один учебный день данных (идемпотентно);
- данные из `bookings.bookings` переливаются в сырой слой `stg.bookings` в Greenplum через PXF‑внешнюю таблицу `stg.bookings_ext`.
- Слой `stg` задуман как «сырой»:
- все бизнес‑колонки (`book_ref`, `book_date`, `total_amount`) хранятся как `TEXT`;
- есть тех.колонки `src_created_at_ts`, `load_dttm`, `batch_id`.
Подробный дизайн описан в `docs/internal/bookings_stg_design.md`.
## 2. Что нужно, чтобы DAG завёлся
Минимальные предпосылки:
- Стенд поднят: `make up`.
- Демо‑БД bookings инициализирована: `make bookings-init`.
- В Greenplum применён DDL (созданы схема `stg` и таблицы `stg.bookings_ext` / `stg.bookings`):
- учебный вариант: запустить DAG `bookings_stg_ddl` (он использует `sql/stg/bookings_ddl.sql`);
- технический шорткат: `make ddl-gp` применяет все DDL разом вручную. Команда сама не вызывается при старте контейнеров, её нужно запустить явно.
- В Airflow есть коннекты:
- `greenplum_conn` — к Greenplum;
- `bookings_db` — к сервису `bookings-db`.
По умолчанию они задаются через переменные окружения `AIRFLOW_CONN_...` в docker-compose,
поэтому могут не отображаться в UI, но `PostgresOperator` найдёт их по `conn_id`. При желании
их можно создать/отредактировать вручную через Admin → Connections.
## 3. Последовательность задач в DAG
- `generate_bookings_day`:
- PostgresOperator к `bookings-db`;
- выполняет SQL `/sql/src/bookings_generate_day_if_missing.sql`;
- скрипт смотрит на `max(book_date)` в `bookings.bookings`:
- если база пустая — берёт стартовую дату из GUC и генерирует `bookings.init_days` суток;
- если данные уже есть — добавляет один следующий учебный день после `max(book_date)` и пишет NOTICE с интервалом генерации.
- `load_bookings_to_stg`:
- PostgresOperator к Greenplum;
- выполняет SQL `/sql/stg/bookings_load.sql`;
- считает «старый» максимум `src_created_at_ts` (по предыдущим батчам) и грузит только новые строки из `stg.bookings_ext`, заполняя `src_created_at_ts`, `load_dttm`, `batch_id={{ ds_nodash }}`.
- `check_row_counts`:
- PostgresOperator к Greenplum;
- выполняет SQL `/sql/stg/bookings_dq.sql`;
- за то же окно инкремента считает количество строк в источнике и в `stg.bookings` (по текущему `batch_id`);
- при расхождении делает `RAISE EXCEPTION` с понятным текстом ошибки.
- `finish_summary`:
- логирует итог выполнения DAG за одно срабатывание.
## 4. Как этим пользоваться студенту (черновой сценарий)
1. Поднять стенд и подготовить источники:
- `make up` (Airflow инициализируется автоматически при первом старте)
- `make bookings-init`
- `make ddl-gp`
2. Открыть Airflow UI (`http://localhost:8080`) и включить DAG `bookings_to_gp_stage`.
3. Вызвать `Trigger` DAG (дату логического запуска можно оставить по умолчанию — она используется только как метка `batch_id`).
4. Посмотреть:
- в `bookings-db` ― что появился день с бронированиями;
- в Greenplum (`make gp-psql`) — данные в `stg.bookings`:
- `SELECT * FROM stg.bookings LIMIT 10;`
- `SELECT src_created_at_ts, load_dttm, batch_id FROM stg.bookings ORDER BY src_created_at_ts DESC LIMIT 10;`
5. Перезапустить DAG ещё несколько раз и увидеть, что:
- генерация в `bookings.bookings` идёт по одному дню вперёд от текущего `max(book_date)`;
- в `stg.bookings` появляются только новые записи (delta), помеченные разными `batch_id`.
## 5. Примечания «на потом»
- Текущая документация по блоку bookings/STG разбросана:
- `README.md` (общий обзор стенда),
- `docs/internal/bookings_tz.md` (источник bookings),
- `docs/internal/pxf_bookings.md` (PXF),
- `docs/internal/bookings_stg_design.md` (дизайн STG),
- этот файл (мини‑README по DAG).
- В будущем всё это нужно будет собрать в одну понятную историю для студента:
- отдельный раздел «Учебный пример: bookings → stg → dwh»;
- скриншоты DAG, примеры запросов и типичные ошибки.
+1 -1
View File
@@ -143,7 +143,7 @@
- `docker-compose.yml` (сервис `greenplum`: `build`, `hostname`, env, healthcheck)
- `pxf/init/10_pxf_bookings.sh` (ensure‑логика)
- `pxf/init/start_greenplum_with_pxf.sh` (старт контейнера)
- `README.md` (раздел «Greenplum + PXF: свой образ»)
- `docs/stack.md` (раздел «Greenplum + PXF: свой образ»)
## 10. Известная проблема: после `docker compose stop/start` Greenplum может упасть (auth для PXF)
+125
View File
@@ -0,0 +1,125 @@
# Технические детали стенда (Docker / Airflow / Greenplum)
Этот документ не обязателен для прохождения лабораторных, но полезен, если вы:
- хотите понять, как устроен стенд “под капотом”;
- меняете конфиги/зависимости и пересобираете образы;
- отлаживаете проблемы со стартом Greenplum/PXF или Connections в Airflow.
## Make (опционально, но удобно)
Если `make` не установлен, можно пользоваться `docker compose ...`.
Но с `make` команды короче и проще.
Установка `make` (если нужно):
- Linux (Debian/Ubuntu): `sudo apt install -y make`
- macOS: `brew install make`
- Windows:
- WSL: `sudo apt install -y make`
- Chocolatey: `choco install make`
- Scoop: `scoop install make`
Примеры:
```bash
make up
make logs
make gp-psql
```
## Airflow Connections
Готовые DAG по умолчанию подключаются к БД через Airflow Connections:
- `greenplum_conn` — Greenplum;
- `bookings_db` — демо‑БД bookings (`bookings-db`).
В `docker-compose.yml` Connections задаются через переменные окружения `AIRFLOW_CONN_...`,
поэтому они могут не отображаться в UI — для DAG это нормально.
Если нужно завести вручную:
1) Airflow UI → Admin → Connections → Add a new record
2) Пример для Greenplum:
- Conn Id: `greenplum_conn`
- Conn Type: `Postgres`
- Host: `greenplum`
- Schema: `gp_dwh`
- Login/Password: `gpadmin` / `gpadmin`
- Port: `5432`
## Greenplum + PXF: свой образ
Greenplum собирается из собственного `Dockerfile.greenplum`, чтобы:
- не ловить проблемы с правами/`chown` при bind‑mount конфигов;
- держать PXF‑конфиги и JDBC‑драйвер внутри образа;
- делать старт контейнера идемпотентным.
При старте контейнера:
- seed‑конфиги PXF докладываются в `PXF_BASE` на persistent volume;
- создаются каталоги `PXF_BASE/run` и `PXF_BASE/logs`;
- расширение `pxf` создаётся автоматически, когда Greenplum становится доступен (с ретраями).
Полезные команды:
```bash
make build # пересобрать образы
docker compose ps # проверить health
```
Перезапись seed‑файлов в `PXF_BASE`:
- `PXF_SEED_OVERWRITE=1` — перезаписать конфиги при старте;
- `PXF_SYNC_ON_START=1` — выполнить `pxf cluster sync` при старте.
Подробнее: `docs/internal/pxf_bookings.md`.
## Airflow: свой образ
Airflow тоже собирается из собственного `Dockerfile.airflow`, чтобы зависимости ставились при сборке,
а не во время старта контейнеров.
Если меняли `airflow/requirements.txt` или `Dockerfile.airflow`, пересоберите образы:
```bash
make build
make up
```
## Локальное окружение разработчика (uv)
Локальным окружением управляет `uv` — он создаёт `.venv` из `pyproject.toml` / `uv.lock`.
```bash
uv sync
make test
make lint
make fmt
```
Не устанавливайте зависимости через `pip install --user ...` — это часто ломает окружение и IDE.
## Переменные окружения (`.env`)
Все настройки лежат в `.env` (шаблон: `.env.example`).
### Greenplum
- `GP_USER`, `GP_PASSWORD`, `GP_DB`
- `GP_PORT` — внутренний порт в Docker-сети (обычно `5432`), внешний порт на хосте фиксирован на `5435`
### bookings-db (Postgres)
- `BOOKINGS_DB_USER`, `BOOKINGS_DB_PASSWORD`
- `BOOKINGS_DB_PORT` — внешний порт (по умолчанию `5434`)
- `BOOKINGS_START_DATE` — стартовая дата модельного времени
- `BOOKINGS_INIT_DAYS` — сколько дней генерировать при первом `make bookings-init`
- `BOOKINGS_JOBS` — число джобов генератора (по умолчанию `1`)
### CSV pipeline (побочный пример)
- `CSV_DIR` — путь к каталогу с CSV внутри контейнеров Airflow (по умолчанию `/opt/airflow/data`)
- `CSV_ROWS` — количество строк, генерируемых DAG (по умолчанию `1000`)
+3 -3
View File
@@ -72,7 +72,7 @@
### 2.2. Знакомство с STG в Greenplum
1. Прочитайте `sql/stg/bookings_ddl.sql` и мини‑README `docs/internal/bookings_stg_readme.md` (если интересно — `docs/internal/bookings_stg_design.md`).
1. Прочитайте `sql/stg/bookings_ddl.sql` и краткое описание потока `docs/bookings_to_gp_stage.md` (если интересно — `docs/internal/bookings_stg_design.md`).
2. Ответьте себе на вопросы:
- чем внешняя таблица `stg.bookings_ext` отличается от внутренней `stg.bookings`;
- зачем нужны тех.колонки `src_created_at_ts`, `load_dttm`, `batch_id`;
@@ -111,12 +111,12 @@
- `sql/src/bookings_generate_day_if_missing.sql`
- `sql/stg/bookings_load.sql`
- `sql/stg/bookings_dq.sql`
3. Соотнесите шаги DAG с документом `docs/internal/bookings_stg_readme.md`:
3. Соотнесите шаги DAG с документом `docs/bookings_to_gp_stage.md`:
- генерация учебного дня в `bookings.bookings`;
- загрузка инкремента в `stg.bookings`;
- проверка количества строк между источником и STG.
4. Обратите внимание, как в DAG используется логическая дата запуска:
- `{{ ds_nodash }}` используется как `batch_id` — метка загрузки в таблице `stg.bookings` для конкретного запуска;
- `{{ run_id }}` используется как `batch_id` — метка загрузки в таблице `stg.bookings` для конкретного запуска;
- сами даты данных (какие дни есть в `bookings.bookings`) определяются генератором по `max(book_date)`, а не по `ds`.
На этом этапе достаточно понять общую цепочку. Детальные задания по переработке модели данных и построению ODS/DDS/DM слоёв будут добавлены позже.
+1 -1
View File
@@ -6,7 +6,7 @@
-- Чтобы не ломать задания, новые объекты лучше добавлять в отдельные файлы
-- и подключать их отсюда, а существующие определения не удалять.
--
-- Подробнее про STG/bookings: см. docs/internal/bookings_stg_readme.md.
-- Подробнее про STG/bookings: см. docs/bookings_to_gp_stage.md.
-- Таблица для CSV‑пайплайна (csv_to_greenplum).
\i base/orders_ddl.sql