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
+39 -30
View File
@@ -44,8 +44,10 @@ flowchart LR
S2[location_raw]
S3[device_raw]
S4[geo_raw]
MV1[mv_*_to_ods]
MV2[mv_*_to_errors]
end
subgraph AF["⚙️ Airflow"]
B1[load_ods<br/>sql/ods/20_stg_to_ods.sql]
end
subgraph ODS["🔧 ODS"]
@@ -68,12 +70,20 @@ flowchart LR
DM4[v_top_pages]
end
BE --> K1 --> S1 --> MV1 --> O1 --> DE1 --> DM1
LE --> K2 --> S2 --> MV1 --> O2 --> DE1
DE --> K3 --> S3 --> MV1 --> O3 --> DC1 --> DM1
GE --> K4 --> S4 --> MV1 --> O4 --> DC1
S1 & S2 & S3 & S4 --> MV2 -.-> OE
BE --> K1 --> S1
LE --> K2 --> S2
DE --> K3 --> S3
GE --> K4 --> S4
S1 & S2 & S3 & S4 -->|Batch SQL| B1
B1 --> O1 & O2 & O3 & O4
B1 -.-> OE
O1 --> DE1
O2 --> DE1
O3 --> DC1
O4 --> DC1
DE1 --> DM1
DC1 --> DM1
DE1 --> DM2 & DM3 & DM4
DC1 --> DM2 & DM3 & DM4
```
@@ -106,7 +116,7 @@ flowchart TB
DM_T["VIEW для BI<br/>(Superset/Grafana)"]
end
RAW -->|kafka-console-producer| KAFKA -->|MV| STG_T -->|MV| ODS_T
RAW -->|kafka-console-producer| KAFKA -->|MV| STG_T -->|Batch SQL (Airflow)| ODS_T
ODS_T -->|argMax + JOIN| DDS_T -->|VIEW| DM_T
ODS_T -.->|ошибки| DQ
```
@@ -159,14 +169,14 @@ CREATE TABLE stg.browser_raw (
| `geo_by_click` | click_id | ReplacingMergeTree(src_ingest_ts) | Гео-данные |
| `*_errors` | — | MergeTree | Строки с битыми ключами |
**Materialized Views для обработки ошибок:**
**Batch шаг наполнения ODS:**
| MV | Назначение |
|----|-----------|
| `mv_browser_raw_to_ods_errors` | Переносит строки с ошибками в `browser_event_errors` |
| `mv_location_raw_to_ods_errors` | Переносит строки с ошибками в `location_event_errors` |
| `mv_device_raw_to_ods_errors` | Переносит строки с ошибками в `device_by_click_errors` |
| `mv_geo_raw_to_ods_errors` | Переносит строки с ошибками в `geo_by_click_errors` |
| Шаг | Назначение |
|-----|-----------|
| `load_ods` (`sql/ods/20_stg_to_ods.sql`) | Полная пересборка ODS из STG в рамках DAG `etl_pipeline` |
| `TRUNCATE ods.*` | Очистка перед пересборкой для детерминированного результата |
| `INSERT ... SELECT` | Типизация валидных строк в основные ODS таблицы |
| `INSERT ... SELECT` в `*_errors` | Сохранение строк с критичными ошибками парсинга |
**Логика разделения:**
- **Основная таблица**: строки с валидными ключами (`WHERE key IS NOT NULL`)
@@ -203,7 +213,7 @@ GROUP BY error;
**Почему так:**
- **Изоляция источников**: изменения в одном не ломают другие
- **Версионирование**: `ReplacingMergeTree` хранит последнюю версию по `src_ingest_ts`
- **Nullable ключи**: `allow_nullable_key = 1` позволяет хранить "битые" строки
- **Управляемость**: шаг `load_ods` виден в Airflow, есть task-level мониторинг и ретраи
---
@@ -356,6 +366,7 @@ FROM ...
sequenceDiagram
participant User as Пользователь
participant Make as Makefile
participant Airflow as Airflow
participant K as Kafka
participant CH as ClickHouse
participant STG as stg.*_raw
@@ -371,7 +382,7 @@ sequenceDiagram
User->>Make: make ddl
Make->>CH: sql/ddl/00_databases.sql
Make->>CH: sql/ddl/stg/10_stg.sql (Kafka Engine)
Make->>CH: sql/ddl/ods/20_ods.sql (MV)
Make->>CH: sql/ddl/ods/20_ods.sql (таблицы ODS + drop legacy MV)
Make->>CH: sql/ddl/dds/30_dds.sql
Make->>CH: sql/ddl/dm/40_dm.sql
CH-->>User: ✅ Структура БД создана
@@ -384,17 +395,17 @@ sequenceDiagram
end
K->>CH: Потребление сообщений
CH->>STG: INSERT через MV
STG->>ODS: INSERT через MV (типизация)
K-->>User: ✅ Данные в Kafka
CH-->>User: ✅ Данные в STG/ODS
CH-->>User: ✅ Данные в STG
User->>Make: make transform
Make->>CH: sql/dds/30_ods_to_dds.sql
User->>Airflow: Trigger etl_pipeline
Airflow->>CH: sql/ods/20_stg_to_ods.sql
Airflow->>CH: sql/dds/30_ods_to_dds.sql
CH->>ODS: argMax() — снапшот
CH->>DDS: JOIN + INSERT
Make->>CH: sql/dm/40_dds_to_dm.sql
Airflow->>CH: sql/dm/40_dds_to_dm.sql
CH->>DM: DQ summary
CH-->>User: ✅ DDS/DM обновлены
CH-->>User: ✅ ODS/DDS/DM обновлены
```
---
@@ -506,7 +517,7 @@ flowchart LR
**Решение:**
1. Включаем `allow_nullable_key = 1` в `ReplacingMergeTree`
2. Фильтруем NULL в MV (`WHERE key IS NOT NULL` → основная таблица)
2. Фильтруем NULL в batch SQL (`WHERE key IS NOT NULL` → основная таблица)
3. Отдельные `*_errors` таблицы для NULL-ключей
### Почему `ReplacingMergeTree`?
@@ -528,18 +539,16 @@ flowchart LR
**Решение:**
1. **Основная таблица**: только валидные строки (`WHERE key IS NOT NULL`)
2. **Таблица ошибок**: строки с невалидными ключами через отдельные MV
2. **Таблица ошибок**: строки с невалидными ключами через отдельные `INSERT ... SELECT`
3. **DQ-метрики**: массив `parse_errors` для аудита
```sql
-- Основная таблица
CREATE MV mv_browser_raw_to_ods_browser_event
TO ods.browser_event
INSERT INTO ods.browser_event
SELECT ... FROM stg.browser_raw WHERE event_id IS NOT NULL;
-- Таблица ошибок
CREATE MV mv_browser_raw_to_ods_errors
TO ods.browser_event_errors
INSERT INTO ods.browser_event_errors
SELECT ... FROM stg.browser_raw WHERE event_id IS NULL;
```