docs(airflow): update dag implementation plan for mvp
Consolidate the orchestration strategy by merging `kafka_load` and `etl_batch_transform` into a unified `etl_pipeline`. Replace BashOperator dependencies on Kafka CLI with PythonOperators utilizing `kafka-python`. Add detailed technical specifications for helper functions, MVP stages, and validation checks to align with current infrastructure constraints.
This commit is contained in:
+153
-238
@@ -1,271 +1,186 @@
|
|||||||
# План миграции ETL-оркестрации на Airflow
|
# План развития Airflow DAG'ов
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
Заменить `make`-команды на полноценную оркестрацию через Airflow DAGs с сохранением логики пайплайна STG → ODS → DDS → DM.
|
Перевести оркестрацию ETL на Airflow так, чтобы пайплайн оставался устойчивым к "грязным" данным и соответствовал текущей цели проекта: Kafka → ClickHouse (STG → ODS → DDS → DM) → витрины для BI.
|
||||||
|
|
||||||
---
|
## Что важно учесть в текущем репозитории
|
||||||
|
- В `AGENTS.md` как quick check ожидается DAG `etl_pipeline`.
|
||||||
|
- В Airflow-контейнере сейчас нет Kafka CLI, поэтому `kafka-topics.sh` и `kafka-console-producer.sh` из `BashOperator` использовать нельзя без донастройки образа.
|
||||||
|
- DDL должен выполняться строго последовательно: `00 -> 10 -> 20 -> 30 -> 40`.
|
||||||
|
- Файл `jobs/30_dds_refresh.sql` уже включает обе загрузки (`dds.click` и `dds.event`), поэтому в MVP это одна task.
|
||||||
|
|
||||||
## Архитектура потока данных (напоминание)
|
## Архитектура оркестрации
|
||||||
|
### DAG 1 (обязательный): `ddl_init`
|
||||||
|
- `schedule`: `None` (только ручной запуск).
|
||||||
|
- `catchup`: `False`.
|
||||||
|
- `max_active_runs`: `1`.
|
||||||
|
- `is_paused_upon_creation`: `True`.
|
||||||
|
- `tags`: `["ddl", "bootstrap", "clickhouse"]`.
|
||||||
|
|
||||||
```
|
### DAG 2 (обязательный): `etl_pipeline`
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
- `schedule`: `None` (ручной запуск для демо).
|
||||||
│ Источник: data/*.jsonl → Kafka → STG (Kafka Engine) → ODS (MV) │
|
- `catchup`: `False`.
|
||||||
│ │
|
- `max_active_runs`: `1`.
|
||||||
│ STG → ODS: real-time через Materialized Views (не требует оркестрации) │
|
- `tags`: `["etl", "clickhouse", "kafka", "demo"]`.
|
||||||
│ ODS → DDS: batch через SQL (argMax + JOIN) — требует оркестрации │
|
|
||||||
│ DDS → DM: batch через SQL (TRUNCATE + INSERT) — требует оркестрации │
|
### DAG 3 (опциональный): `dq_monitor`
|
||||||
└─────────────────────────────────────────────────────────────────────────────┘
|
- `schedule`: `0 * * * *`.
|
||||||
|
- `catchup`: `False`.
|
||||||
|
- `tags`: `["dq", "monitoring"]`.
|
||||||
|
|
||||||
|
## Дизайн DAG `ddl_init`
|
||||||
|
### Params
|
||||||
|
- `verify_only`: bool, default `false` (прогон только проверок без применения DDL).
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
| Task ID | Что делает | Источник SQL/реализация |
|
||||||
|
|---------|------------|--------------------------|
|
||||||
|
| `check_clickhouse` | Проверка доступности CH (`SELECT 1`) | `PythonOperator` + `clickhouse-connect` |
|
||||||
|
| `ddl_00_databases` | Создание БД | `ddl/00_databases.sql` |
|
||||||
|
| `ddl_10_stg` | STG + Kafka Engine + MV | `ddl/10_stg.sql` |
|
||||||
|
| `ddl_20_ods` | ODS + MV STG→ODS + *_errors | `ddl/20_ods.sql` |
|
||||||
|
| `ddl_30_dds` | Таблицы DDS | `ddl/30_dds.sql` |
|
||||||
|
| `ddl_40_dm` | VIEW витрины DM | `ddl/40_dm.sql` |
|
||||||
|
| `verify_schema` | Проверка ключевых таблиц/VIEW | SQL-check |
|
||||||
|
|
||||||
|
Зависимости:
|
||||||
|
```text
|
||||||
|
check_clickhouse >> ddl_00_databases >> ddl_10_stg >> ddl_20_ods >> ddl_30_dds >> ddl_40_dm >> verify_schema
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Примечание:
|
||||||
|
- DDL DAG запускается вручную: при первом bootstrap, при изменении схемы, после `docker compose down -v`.
|
||||||
|
|
||||||
## Структура DAG'ов
|
## Дизайн DAG `etl_pipeline`
|
||||||
|
### Params (через Trigger DAG with config)
|
||||||
|
- `run_ingest`: bool, default `true`.
|
||||||
|
- `limit`: int, default `50`.
|
||||||
|
- `full_load`: bool, default `false`.
|
||||||
|
- `reset_topics`: bool, default `true`.
|
||||||
|
- `full_refresh`: bool, default `true`.
|
||||||
|
|
||||||
### 1. `ddl_init` — Инициализация схемы БД
|
### TaskGroup `precheck`
|
||||||
|
| Task ID | Что делает | Реализация |
|
||||||
|
|---------|------------|------------|
|
||||||
|
| `check_clickhouse` | Проверка доступности CH (`SELECT 1`) | `PythonOperator` + `clickhouse-connect` |
|
||||||
|
| `check_schema_ready` | Проверка, что DDL уже применён (наличие `stg.browser_raw`, `ods.browser_event`, `dds.event`, `dm.v_events_enriched`) | SQL-check, fail fast |
|
||||||
|
| `check_input_files` | Проверка наличия `data/*_events.jsonl` | `PythonOperator` |
|
||||||
|
|
||||||
**Назначение:** Создание всех объектов ClickHouse (базы, таблицы, MV, витрины).
|
### TaskGroup `ingest` (выполняется только при `run_ingest=true`)
|
||||||
|
| Task ID | Что делает | Реализация |
|
||||||
|
|---------|------------|------------|
|
||||||
|
| `kafka_prepare_topics` | reset/create топиков по параметру `reset_topics` | `PythonOperator` + `kafka-python` AdminClient |
|
||||||
|
| `load_browser` | Публикация строк из `browser_events.jsonl` | `PythonOperator` + `KafkaProducer` |
|
||||||
|
| `load_location` | Публикация строк из `location_events.jsonl` | `PythonOperator` + `KafkaProducer` |
|
||||||
|
| `load_device` | Публикация строк из `device_events.jsonl` | `PythonOperator` + `KafkaProducer` |
|
||||||
|
| `load_geo` | Публикация строк из `geo_events.jsonl` | `PythonOperator` + `KafkaProducer` |
|
||||||
|
| `wait_for_stg_data` | Ожидание появления данных в `stg.*_raw` | `PythonSensor`/poll SQL |
|
||||||
|
|
||||||
**Параметры:**
|
Зависимости:
|
||||||
- `schedule`: `None` (только ручной запуск)
|
```text
|
||||||
- `tags`: `["ddl", "init", "clickhouse"]`
|
kafka_prepare_topics >> [load_browser, load_location, load_device, load_geo] >> wait_for_stg_data
|
||||||
|
|
||||||
**Задачи (Tasks):**
|
|
||||||
|
|
||||||
| Task ID | Описание | Тип оператора |
|
|
||||||
|---------|----------|---------------|
|
|
||||||
| `check_clickhouse` | Проверка доступности ClickHouse | `BashOperator` — `clickhouse-client --query="SELECT 1"` |
|
|
||||||
| `create_databases` | Создание БД: stg, ods, dds, dm | `SQLExecuteQueryOperator` — `ddl/00_databases.sql` |
|
|
||||||
| `create_stg` | Таблицы STG + Kafka Engine + MV | `SQLExecuteQueryOperator` — `ddl/10_stg.sql` |
|
|
||||||
| `create_ods` | Таблицы ODS + MV STG→ODS | `SQLExecuteQueryOperator` — `ddl/20_ods.sql` |
|
|
||||||
| `create_dds` | Таблицы DDS (batch-загрузка) | `SQLExecuteQueryOperator` — `ddl/30_dds.sql` |
|
|
||||||
| `create_dm` | Витрины DM (VIEW) | `SQLExecuteQueryOperator` — `ddl/40_dm.sql` |
|
|
||||||
| `verify_schema` | Проверка: список созданных таблиц | `BashOperator` — запрос `SHOW TABLES FROM each DB` |
|
|
||||||
|
|
||||||
**Зависимости:**
|
|
||||||
```
|
|
||||||
check_clickhouse >> create_databases >> [create_stg, create_ods, create_dds, create_dm] >> verify_schema
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Замечание:** Порядок важен — сначала `stg`+`ods` (MV работают сразу), потом `dds`+`dm`.
|
Примечание:
|
||||||
|
- Для демо соблюдать ограничение на малый срез данных: `limit=20..50` по умолчанию.
|
||||||
|
|
||||||
---
|
### TaskGroup `transform`
|
||||||
|
| Task ID | Что делает | Источник SQL |
|
||||||
|
|---------|------------|--------------|
|
||||||
|
| `wait_for_ods_data` | Ожидание строк в `ods.browser_event` | SQL-check |
|
||||||
|
| `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 |
|
||||||
|
| `refresh_dds` | ODS → DDS | `jobs/30_dds_refresh.sql` |
|
||||||
|
| `check_dds_integrity` | Проверка orphan событий | inline SQL |
|
||||||
|
| `refresh_dm_summary` | DDS → DM DQ summary | `jobs/40_dm_refresh.sql` |
|
||||||
|
| `validate_dm_summary` | Проверка, что `dm.dq_summary` не пуста | SQL-check |
|
||||||
|
|
||||||
### 2. `kafka_load` — Загрузка данных в Kafka
|
Зависимости:
|
||||||
|
```text
|
||||||
**Назначение:** Загрузка JSON-данных из `data/*.jsonl` в Kafka-топики.
|
wait_for_ods_data >> check_ods_quality >> [truncate_dds_click, truncate_dds_event] >> refresh_dds >> check_dds_integrity >> refresh_dm_summary >> validate_dm_summary
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
- `schedule`: `None` (только ручной запуск)
|
|
||||||
- `tags`: `["kafka", "ingest", "demo"]`
|
|
||||||
- `params`:
|
|
||||||
- `limit`: int — количество строк для загрузки (default: 50, 0 = все)
|
|
||||||
- `full_load`: bool — загрузить полные файлы
|
|
||||||
- `reset_topics`: bool — пересоздать топики (default: true)
|
|
||||||
|
|
||||||
**Задачи (Tasks):**
|
|
||||||
|
|
||||||
| Task ID | Описание | Тип оператора |
|
|
||||||
|---------|----------|---------------|
|
|
||||||
| `check_kafka` | Проверка доступности Kafka | `BashOperator` — `kafka-topics.sh --list` |
|
|
||||||
| `reset_topics` | Удаление/создание топиков (conditional) | `BashOperator` — `kafka-topics.sh --delete/--create` |
|
|
||||||
| `load_browser` | Загрузка browser_events.jsonl | `BashOperator` — `kafka-console-producer.sh` |
|
|
||||||
| `load_location` | Загрузка location_events.jsonl | `BashOperator` — `kafka-console-producer.sh` |
|
|
||||||
| `load_device` | Загрузка device_events.jsonl | `BashOperator` — `kafka-console-producer.sh` |
|
|
||||||
| `load_geo` | Загрузка geo_events.jsonl | `BashOperator` — `kafka-console-producer.sh` |
|
|
||||||
| `verify_load` | Проверка: количество сообщений в топиках | `BashOperator` — `kafka-console-consumer.sh --from-beginning` или проверка через CH |
|
|
||||||
|
|
||||||
**Зависимости:**
|
|
||||||
```
|
|
||||||
check_kafka >> reset_topics >> [load_browser, load_location, load_device, load_geo] >> verify_load
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Особенности:**
|
### Итоговая цепочка `etl_pipeline`
|
||||||
- Загрузка файлов может идти параллельно (независимые топики).
|
```text
|
||||||
- `head -n {{ params.limit }}` для среза данных.
|
precheck >> ingest(optional) >> transform
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. `etl_batch_transform` — Batch-трансформация ODS → DDS → DM
|
|
||||||
|
|
||||||
**Назначение:** Основной ETL-пайплайн — сборка сущностей и обновление витрин.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
- `schedule`: `"@once"` для демо или `"*/15 * * * *"` (каждые 15 мин)
|
|
||||||
- `tags`: `["etl", "batch", "dds", "dm"]`
|
|
||||||
- `params`:
|
|
||||||
- `full_refresh`: bool — полная перезагрузка или инкремент (default: true для демо)
|
|
||||||
|
|
||||||
**Задачи (Tasks):**
|
|
||||||
|
|
||||||
| Task ID | Описание | Тип оператора |
|
|
||||||
|---------|----------|---------------|
|
|
||||||
| `wait_for_ods` | Ожидание появления данных в ODS | `BashOperator` — `SELECT count() FROM ods.browser_event` |
|
|
||||||
| `check_ods_quality` | Проверка качества ODS: ошибки парсинга | `SQLExecuteQueryOperator` — `SELECT layer, count() FROM ods.browser_event WHERE ...` |
|
|
||||||
| `truncate_dds` | Очистка DDS таблиц (conditional) | `SQLExecuteQueryOperator` — `TRUNCATE TABLE dds.click, dds.event` |
|
|
||||||
| `refresh_dds_click` | Загрузка dds.click (device + geo) | `SQLExecuteQueryOperator` — `jobs/30_dds_refresh.sql` (часть для click) |
|
|
||||||
| `refresh_dds_event` | Загрузка dds.event (browser + location) | `SQLExecuteQueryOperator` — `jobs/30_dds_refresh.sql` (часть для event) |
|
|
||||||
| `check_dds_integrity` | Проверка: orphan events (есть event, нет click) | `SQLExecuteQueryOperator` — `SELECT count() FROM dds.event WHERE click_id NOT IN (...)` |
|
|
||||||
| `refresh_dm_summary` | Обновление dm.dq_summary | `SQLExecuteQueryOperator` — `jobs/40_dm_refresh.sql` |
|
|
||||||
| `validate_dm` | Проверка: dq_summary не пустая | `BashOperator` — `SELECT * FROM dm.dq_summary` |
|
|
||||||
|
|
||||||
**Зависимости:**
|
|
||||||
```
|
|
||||||
wait_for_ods >> check_ods_quality >> truncate_dds >> [refresh_dds_click, refresh_dds_event] >> check_dds_integrity >> refresh_dm_summary >> validate_dm
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Особенности:**
|
## Техническая реализация (приземленно)
|
||||||
- `refresh_dds_click` и `refresh_dds_event` независимы — можно параллельно.
|
### ClickHouse в Airflow
|
||||||
- Для инкрементальной загрузки (в будущем) понадобится watermark (src_ingest_ts).
|
- Использовать `clickhouse-connect` напрямую в Python helper, а не `SQLExecuteQueryOperator`.
|
||||||
|
- Брать параметры подключения из `conn_id = clickhouse_default` через `BaseHook.get_connection`.
|
||||||
|
|
||||||
---
|
### Kafka в Airflow
|
||||||
|
- Добавить зависимость `kafka-python` в `airflow/requirements.txt`.
|
||||||
|
- Использовать Python-код для:
|
||||||
|
- reset/create топиков;
|
||||||
|
- публикации строк из `.jsonl` (1 строка = 1 message value).
|
||||||
|
|
||||||
### 4. `data_quality_monitor` — Мониторинг качества данных (опциональный)
|
### Общие helper-функции
|
||||||
|
- `dags/utils/clickhouse_helpers.py`:
|
||||||
|
- `execute_sql(sql: str) -> None`
|
||||||
|
- `execute_sql_file(path: str) -> None`
|
||||||
|
- `fetch_one(sql: str) -> tuple`
|
||||||
|
- `dags/utils/kafka_helpers.py`:
|
||||||
|
- `prepare_topics(reset: bool) -> None`
|
||||||
|
- `load_jsonl(file_path: str, topic: str, limit: int, full_load: bool) -> int`
|
||||||
|
|
||||||
**Назначение:** Регулярная проверка DQ-метрик и алерты.
|
## Структура файлов
|
||||||
|
```text
|
||||||
**Параметры:**
|
|
||||||
- `schedule`: `"0 */1 * * *"` (каждый час)
|
|
||||||
- `tags`: `["dq", "monitoring", "alerts"]`
|
|
||||||
|
|
||||||
**Задачи (Tasks):**
|
|
||||||
|
|
||||||
| Task ID | Описание | Тип оператора |
|
|
||||||
|---------|----------|---------------|
|
|
||||||
| `check_stg_volume` | Проверка объёма STG | `SQLExecuteQueryOperator` |
|
|
||||||
| `check_ods_errors` | Проверка ошибок парсинга ODS | `SQLExecuteQueryOperator` — `SELECT count() FROM ods.*_errors` |
|
|
||||||
| `check_dds_orphans` | Проверка сиротских записей | `SQLExecuteQueryOperator` |
|
|
||||||
| `send_alert` | Отправка алерта (если проблемы) | `EmptyOperator` или callback |
|
|
||||||
|
|
||||||
**Зависимости:** Линейная цепочка с условными переходами.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Технические детали реализации
|
|
||||||
|
|
||||||
### Подключение к ClickHouse
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Connection в Airflow UI (Admin → Connections)
|
|
||||||
conn_id = "clickhouse_default"
|
|
||||||
conn_type = "generic"
|
|
||||||
host = "clickhouse"
|
|
||||||
port = 8123 # HTTP interface
|
|
||||||
login = "default"
|
|
||||||
password = "123456"
|
|
||||||
```
|
|
||||||
|
|
||||||
### SQL-операторы
|
|
||||||
|
|
||||||
Для выполнения SQL использовать `SQLExecuteQueryOperator` с `clickhouse-connect`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
|
|
||||||
|
|
||||||
refresh_dds = SQLExecuteQueryOperator(
|
|
||||||
task_id="refresh_dds_click",
|
|
||||||
conn_id="clickhouse_default",
|
|
||||||
sql="""
|
|
||||||
INSERT INTO dds.click
|
|
||||||
SELECT ... -- SQL из jobs/30_dds_refresh.sql
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bash-операторы для Kafka
|
|
||||||
|
|
||||||
```python
|
|
||||||
from airflow.operators.bash import BashOperator
|
|
||||||
|
|
||||||
load_browser = BashOperator(
|
|
||||||
task_id="load_browser",
|
|
||||||
bash_command="""
|
|
||||||
head -n {{ params.limit }} /opt/airflow/data/browser_events.jsonl | \
|
|
||||||
kafka-console-producer.sh --bootstrap-server kafka:29092 --topic browser_events
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Сенсоры (Sensors)
|
|
||||||
|
|
||||||
Для ожидания данных использовать `SqlSensor`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from airflow.providers.common.sql.sensors.sql import SqlSensor
|
|
||||||
|
|
||||||
wait_for_ods = SqlSensor(
|
|
||||||
task_id="wait_for_ods",
|
|
||||||
conn_id="clickhouse_default",
|
|
||||||
sql="SELECT count() > 0 FROM ods.browser_event",
|
|
||||||
mode="poke",
|
|
||||||
poke_interval=30,
|
|
||||||
timeout=600
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Последовательность внедрения
|
|
||||||
|
|
||||||
1. **Этап 1: DDL и Batch**
|
|
||||||
- Создать `ddl_init` DAG
|
|
||||||
- Создать `etl_batch_transform` DAG
|
|
||||||
- Проверить полный цикл: DDL → load (ручной) → transform
|
|
||||||
|
|
||||||
2. **Этап 2: Kafka Load**
|
|
||||||
- Создать `kafka_load` DAG с параметрами
|
|
||||||
- Интегрировать с `etl_batch_transform` через TriggerDagRunOperator
|
|
||||||
|
|
||||||
3. **Этап 3: Мониторинг**
|
|
||||||
- Добавить `data_quality_monitor` DAG
|
|
||||||
- Настроить алерты (email/Slack)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Файловая структура
|
|
||||||
|
|
||||||
```
|
|
||||||
dags/
|
dags/
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
├── ddl_init.py # DAG #1: Инициализация схемы
|
├── ddl_init_dag.py # отдельный DAG для DDL (обязателен)
|
||||||
├── kafka_load.py # DAG #2: Загрузка в Kafka
|
├── etl_pipeline_dag.py # основной ETL DAG (обязателен)
|
||||||
├── etl_batch_transform.py # DAG #3: Batch ETL
|
├── dq_monitor_dag.py # опциональный DAG мониторинга
|
||||||
├── data_quality_monitor.py # DAG #4: DQ мониторинг (опционально)
|
└── utils/
|
||||||
├── utils/
|
├── __init__.py
|
||||||
│ ├── __init__.py
|
├── clickhouse_helpers.py
|
||||||
│ ├── clickhouse_helpers.py # Общие функции для CH
|
└── kafka_helpers.py
|
||||||
│ └── kafka_helpers.py # Общие функции для Kafka
|
|
||||||
└── sql/ # SQL-шаблоны (опционально)
|
|
||||||
├── dds_click_insert.sql
|
|
||||||
├── dds_event_insert.sql
|
|
||||||
└── dm_summary_insert.sql
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Этапы внедрения
|
||||||
|
1. Этап 1 (MVP, обязательно):
|
||||||
|
- Реализовать `ddl_init` и `etl_pipeline`.
|
||||||
|
- В `etl_pipeline` оставить `precheck + transform`, ingest пока выполнять внешней командой `make data`.
|
||||||
|
- Проверить путь: `ddl_init` -> ODS -> DDS -> DM после ручной загрузки в Kafka.
|
||||||
|
2. Этап 2:
|
||||||
|
- Добавить в `etl_pipeline` ingest внутри DAG через `kafka-python`.
|
||||||
|
- Добавить ветвление `run_ingest=false` для сценария "только transform".
|
||||||
|
3. Этап 3:
|
||||||
|
- Добавить `dq_monitor` и alert callback (email/Slack/webhook).
|
||||||
|
|
||||||
## Особенности и ограничения
|
## Критерии готовности
|
||||||
|
- В Airflow UI видны DAG `ddl_init` и `etl_pipeline`.
|
||||||
1. **STG → ODS:** Работает через MV автоматически, не требует DAG.
|
- `etl_pipeline` падает с понятной ошибкой, если схема не применена.
|
||||||
2. **Очистка Kafka:** MV в ClickHouse запоминают offset'ы — для чистого старта нужно пересоздать MV.
|
- Ручной запуск DAG с `limit=50` завершает pipeline без падений.
|
||||||
3. **Полная перезагрузка:** Для демо используем `TRUNCATE + INSERT`. В продакшене — инкремент.
|
- После прогона:
|
||||||
4. **Зависимости сервисов:** DAG'и должны проверять доступность ClickHouse/Kafka перед работой.
|
- в `ods.browser_event` есть строки;
|
||||||
5. **Идемпотентность:** Batch-задачи должны быть идемпотентны (TRUNCATE перед INSERT).
|
- в `dds.click` и `dds.event` есть строки;
|
||||||
|
- `dm.dq_summary` заполнена.
|
||||||
---
|
- При повторном запуске с `full_refresh=true` нет неконтролируемых дублей в DDS.
|
||||||
|
|
||||||
## Проверка после реализации
|
|
||||||
|
|
||||||
|
## Минимальные smoke-checks
|
||||||
```bash
|
```bash
|
||||||
# 1. Запуск Airflow
|
# 1) Запуск инфраструктуры
|
||||||
docker compose up -d airflow
|
make up
|
||||||
|
|
||||||
# 2. В UI должны появиться DAG'и: ddl_init, kafka_load, etl_batch_transform
|
# 2) Открыть Airflow UI
|
||||||
|
# http://localhost:8080 (admin/admin)
|
||||||
|
|
||||||
# 3. Тестовый прогон:
|
# 3) Один раз запустить ddl_init
|
||||||
# - Trigger ddl_init → проверить таблицы в CH
|
# Trigger DAG ddl_init (без config или {"verify_only": false})
|
||||||
# - Trigger kafka_load (limit=50) → проверить топики
|
|
||||||
# - Дождаться появления данных в ODS (автоматически через MV)
|
|
||||||
# - Trigger etl_batch_transform → проверить DDS и DM
|
|
||||||
|
|
||||||
# 4. Проверка результатов:
|
# 4) Trigger DAG etl_pipeline с config:
|
||||||
docker compose exec clickhouse clickhouse-client -q "SELECT * FROM dm.dq_summary"
|
# {"run_ingest": true, "limit": 50, "full_load": false, "reset_topics": true, "full_refresh": true}
|
||||||
|
|
||||||
|
# 5) Проверка результатов
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "SELECT count() FROM ods.browser_event"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "SELECT count() FROM dds.click"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "SELECT count() FROM dds.event"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "SELECT count() FROM dm.dq_summary"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Следующий шаг после MVP
|
||||||
|
- Для ускорения можно разделить `jobs/30_dds_refresh.sql` на два файла и распараллелить `refresh_dds_click` и `refresh_dds_event` в DAG.
|
||||||
|
- Для продакшн-режима перейти с `full_refresh` на watermark-инкремент.
|
||||||
|
|||||||
Reference in New Issue
Block a user