Move STG->ODS to Airflow batch and align monitoring

This commit is contained in:
2026-02-08 16:36:01 +03:00
parent a7963c17b3
commit 284dc3dc1f
10 changed files with 536 additions and 335 deletions
+7 -6
View File
@@ -46,7 +46,7 @@
| `check_clickhouse` | Проверка доступности CH (`SELECT 1`) | `PythonOperator` + `clickhouse-connect` |
| `ddl_00_databases` | Создание БД | `sql/ddl/00_databases.sql` |
| `ddl_10_stg` | STG + Kafka Engine + MV | `sql/ddl/stg/10_stg.sql` |
| `ddl_20_ods` | ODS + MV STG→ODS + *_errors | `sql/ddl/ods/20_ods.sql` |
| `ddl_20_ods` | ODS таблицы + drop legacy MV STG→ODS | `sql/ddl/ods/20_ods.sql` |
| `ddl_30_dds` | Таблицы DDS | `sql/ddl/dds/30_dds.sql` |
| `ddl_40_dm` | VIEW витрины DM | `sql/ddl/dm/40_dm.sql` |
| `verify_schema` | Проверка ключевых таблиц/VIEW | SQL-check |
@@ -98,7 +98,7 @@ precheck >> prepare_topics >> [load_browser_events, load_location_events, load_d
## Дизайн DAG `etl_pipeline`
### Params (через Trigger DAG with config)
- `full_refresh`: bool, default `true`.
- `wait_ods_timeout_sec`: int, default `600`.
- `wait_stg_timeout_sec`: int, default `600`.
### TaskGroup `precheck`
| Task ID | Что делает | Реализация |
@@ -109,7 +109,8 @@ precheck >> prepare_topics >> [load_browser_events, load_location_events, load_d
### TaskGroup `transform`
| Task ID | Что делает | Источник SQL |
|---------|------------|--------------|
| `wait_for_ods_data` | Ожидание строк в `ods.browser_event` | SQL-check |
| `wait_for_stg_data` | Ожидание строк в `stg.*_raw` | SQL-check |
| `load_ods` | Batch STG → ODS (основные таблицы + *_errors) | `sql/ods/20_stg_to_ods.sql` |
| `check_ods_quality` | Базовые DQ-метрики ODS (ошибки/total) | SQL-check |
| `truncate_dds_click` | Очистка `dds.click` при `full_refresh=true` | inline SQL |
| `truncate_dds_event` | Очистка `dds.event` при `full_refresh=true` | inline SQL |
@@ -120,7 +121,7 @@ precheck >> prepare_topics >> [load_browser_events, load_location_events, load_d
Зависимости:
```text
wait_for_ods_data >> check_ods_quality >> [truncate_dds_click, truncate_dds_event] >> load_dds >> check_dds_integrity >> load_dm_summary >> validate_dm_summary
wait_for_stg_data >> load_ods >> check_ods_quality >> [truncate_dds_click, truncate_dds_event] >> load_dds >> check_dds_integrity >> load_dm_summary >> validate_dm_summary
```
### Итоговая цепочка `etl_pipeline`
@@ -165,7 +166,7 @@ dags/
├── __init__.py
├── ddl_init_dag.py # отдельный DAG для DDL (обязателен)
├── kafka_load_dag.py # отдельный DAG для ingest в Kafka (обязателен)
├── etl_pipeline_dag.py # основной DAG ODS -> DDS -> DM (обязателен)
├── etl_pipeline_dag.py # основной DAG STG -> ODS -> DDS -> DM (обязателен)
├── dq_monitor_dag.py # опциональный DAG мониторинга
└── utils/
├── __init__.py
@@ -189,7 +190,7 @@ dags/
## Критерии готовности
- Этап 1:
- В Airflow UI видны DAG `ddl_init` и `etl_pipeline`.
- `etl_pipeline` падает с понятной ошибкой, если схема не применена или ODS пуста.
- `etl_pipeline` падает с понятной ошибкой, если схема не применена или STG пуста.
- После прогона `make data -> etl_pipeline`:
- в `ods.browser_event` есть строки;
- в `dds.click` и `dds.event` есть строки;
+17 -13
View File
@@ -31,16 +31,16 @@
Эта схема укладывается в задание так:
- **Kafka → STG → ODS** можно сделать полностью внутри ClickHouse через `ENGINE = Kafka` + MV (стриминг, 1 json = 1 row).
- “**регулярный процесс**” — батч‑трансформации `ODS → DDS (→ DM)` в виде SQL (`INSERT INTO … SELECT …`) по расписанию (в будущем Airflow; пока — ручной запуск).
- Airflow (когда появится) можно использовать как **тонкий оркестратор**: применить DDL и запускать batch‑SQL по расписанию.
- **Kafka → STG** реализуется внутри ClickHouse через `ENGINE = Kafka` + MV (стриминг, 1 json = 1 row).
- “**регулярный процесс**” — батч‑трансформации `STG → ODS → DDS (→ DM)` в виде SQL (`INSERT INTO … SELECT …`) по расписанию (Airflow).
- Airflow используется как основной оркестратор batch‑шагов и DQ‑проверок.
### Выбранное решение (MVP)
Чтобы сделать “хорошо, но без оверинжиниринга”, фиксируем такое MVP:
- **STG → ODS** — инкрементально через MV (парсинг/типизация рядом с ingest).
- **DDS** — батч‑сборка из ODS через SQL (`INSERT INTO … SELECT …`) как “регулярный процесс”.
- **STG → ODS** — batch через SQL (`sql/ods/20_stg_to_ods.sql`) внутри `etl_pipeline`.
- **DDS** — batch‑сборка из ODS через SQL (`INSERT INTO … SELECT …`) как “регулярный процесс”.
- Причина: `MV + JOIN` в `ODS → DDS` плохо переносит произвольный порядок прихода данных и может давать некорректные результаты (eventual consistency ODS, версии в разных партициях и т.п.).
- **DM** — `VIEW` (витрины “на чтении”) поверх DDS, чтобы не плодить лишние таблицы и джобы под демо.
- На стороне BI считаем, что запросы всегда идут с фильтрами по времени (`event_date`/`event_ts`) и не сканируют всю историю.
@@ -75,10 +75,10 @@ flowchart LR
v_dq[dm.v_dq_errors_daily]
end
stg_browser -->|MV parse| ods_browser
stg_location -->|MV parse| ods_location
stg_device -->|MV parse| ods_device
stg_geo -->|MV parse| ods_geo
stg_browser -->|Batch SQL| ods_browser
stg_location -->|Batch SQL| ods_location
stg_device -->|Batch SQL| ods_device
stg_geo -->|Batch SQL| ods_geo
ods_browser -->|Batch SQL| dds_event
ods_location -->|Batch SQL| dds_event
@@ -125,7 +125,7 @@ flowchart LR
- `sql/ddl/00_databases.sql` — базы `stg/ods/dds/dm`.
- `sql/ddl/stg/10_stg.sql` — STG raw (`stg.*_raw`) + Kafka source tables (`ENGINE = Kafka`) + MV `Kafka → STG`.
- `sql/ddl/ods/20_ods.sql` — ODS таблицы типизации + DQ (`parse_errors`) + MV `STG → ODS` + таблицы `ods_*_errors` для строк с битыми ключами.
- `sql/ddl/ods/20_ods.sql` — ODS таблицы типизации + DQ (`parse_errors`) + удаление legacy MV `STG → ODS`.
- `sql/ddl/dds/30_dds.sql` — DDS таблицы (`dds.event`, `dds.click`) **без MV** (только `CREATE TABLE`).
- `sql/ddl/dm/40_dm.sql` — витрины `VIEW` для Superset (`dm.v_*`).
@@ -136,13 +136,17 @@ BI-ограничения (ресурсы/пользователь) **не вы
- `sql/dds/30_ods_to_dds.sql` — регулярная батч‑сборка DDS из ODS:
- получить “последнюю версию” строк по ключам (`event_id`/`click_id`) через `argMax(..., src_ingest_ts)` (или эквивалент);
- выполнить join snapshot’ов и загрузить в `dds.event`/`dds.click` (для демо возможно “full rebuild”; позже — инкрементально).
- `sql/ods/20_stg_to_ods.sql` — регулярная батч‑сборка ODS из STG:
- очистить ODS (`TRUNCATE`) перед пересборкой;
- типизировать валидные строки в `ods.*`;
- сложить критичные ошибки парсинга в `ods.*_errors`.
### Исполнение DDL (make сейчас / Airflow потом)
Требования к файлам `sql/*/*.sql`:
- идемпотентность (`IF NOT EXISTS`), чтобы повторные прогоны были безопасны;
- строгий порядок исполнения: `00 → 10 → 20 → 30 → 40` (из‑за зависимостей MV);
- строгий порядок исполнения: `00 → 10 → 20 → 30 → 40` (из‑за зависимостей объектов);
- единые имена топиков Kafka: `browser_events`, `location_events`, `device_events`, `geo_events` (их создаёт `make data`).
### Дедупликация и обработка “битых” ключей (ODS)
@@ -167,8 +171,8 @@ BI-ограничения (ресурсы/пользователь) **не вы
Batch‑трансформации (целевое, для реализации следующим шагом):
- `make transform` (или аналогичная команда) запускает `sql/dds/30_ods_to_dds.sql` через `clickhouse-client`;
- в будущем Airflow будет делать то же самое по расписанию (один job‑SQL = один task).
- `make transform` (или аналогичная команда) запускает `sql/ods/20_stg_to_ods.sql`, `sql/dds/30_ods_to_dds.sql` и `sql/dm/40_dds_to_dm.sql` через `clickhouse-client`;
- Airflow может выполнять те же шаги как отдельные task (с ретраями и мониторингом).
### Параметры окружения (docker compose)
+3 -2
View File
@@ -32,8 +32,9 @@ make ddl
## Make таргеты
- `make up``docker compose up -d` (поднимает весь стек из `docker-compose.yml`).
- `make ddl` — применяет SQL из `plans/clickhouse_ddl.md` в контейнер ClickHouse (извлекает все блоки ```sql``` и исполняет их через `clickhouse-client`).
- `make ddl` — применяет исполняемые SQL-файлы из `sql/ddl/*` в контейнер ClickHouse через `clickhouse-client`.
- `make data` — пересоздаёт топики (по умолчанию) и публикует события из `data/*.jsonl` в Kafka (1 строка = 1 Kafka message value).
- `make transform` — выполняет batch-процесс `STG -> ODS -> DDS -> DM` через `scripts/run_batch.sh`.
План реализации механики заливки (дизайн/решения): `plans/kafka_ingest_plan.md`.
@@ -75,7 +76,7 @@ RESET_TOPICS=0 make data
## Применение DDL в ClickHouse (`make ddl`)
Скрипт исполняет SQL из `plans/clickhouse_ddl.md`. Для `ENGINE = Kafka` важно, чтобы `kafka_broker_list` был доступен из контейнера ClickHouse.
Скрипт исполняет SQL-файлы из `sql/ddl/*` по фиксированному порядку. Для `ENGINE = Kafka` важно, чтобы `kafka_broker_list` был доступен из контейнера ClickHouse.
В текущем compose: