Files
clickstream-ch-kafka-supers…/plans/airflow_dags_plan.md
T
ddadmin 4da6a34e4c 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.
2026-02-07 00:03:32 +03:00

9.8 KiB
Raw Blame History

План развития Airflow DAG'ов

Цель

Перевести оркестрацию 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 (ручной запуск для демо).
  • catchup: False.
  • max_active_runs: 1.
  • tags: ["etl", "clickhouse", "kafka", "demo"].

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

Зависимости:

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 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.

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

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

Зависимости:

kafka_prepare_topics >> [load_browser, load_location, load_device, load_geo] >> wait_for_stg_data

Примечание:

  • Для демо соблюдать ограничение на малый срез данных: 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

Зависимости:

wait_for_ods_data >> check_ods_quality >> [truncate_dds_click, truncate_dds_event] >> refresh_dds >> check_dds_integrity >> refresh_dm_summary >> validate_dm_summary

Итоговая цепочка etl_pipeline

precheck >> ingest(optional) >> transform

Техническая реализация (приземленно)

ClickHouse в Airflow

  • Использовать 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).

Общие 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

Структура файлов

dags/
├── __init__.py
├── ddl_init_dag.py           # отдельный DAG для DDL (обязателен)
├── etl_pipeline_dag.py       # основной ETL DAG (обязателен)
├── dq_monitor_dag.py         # опциональный DAG мониторинга
└── utils/
    ├── __init__.py
    ├── clickhouse_helpers.py
    └── kafka_helpers.py

Этапы внедрения

  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.
  • etl_pipeline падает с понятной ошибкой, если схема не применена.
  • Ручной запуск DAG с limit=50 завершает pipeline без падений.
  • После прогона:
    • в ods.browser_event есть строки;
    • в dds.click и dds.event есть строки;
    • dm.dq_summary заполнена.
  • При повторном запуске с full_refresh=true нет неконтролируемых дублей в DDS.

Минимальные smoke-checks

# 1) Запуск инфраструктуры
make up

# 2) Открыть Airflow UI
# http://localhost:8080 (admin/admin)

# 3) Один раз запустить ddl_init
# Trigger DAG ddl_init (без config или {"verify_only": false})

# 4) Trigger DAG etl_pipeline с config:
# {"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-инкремент.