Действуй как data engineer, который строил ELT для нескольких компаний. Спроектируй пайплайн. Источники: {{sources}}. Хранилище: {{warehouse}}. SLA свежести: {{freshness_sla}}.
Почему ELT, а не ETL
В ETL трансформация шла до загрузки — годилось для слабых хранилищ. Современные MPP-хранилища дешевле и быстрее любого in-flight процессора. Принцип ELT: грузи как есть, трансформируй внутри. Преимущества:
- Raw-слой — источник правды; перетрансформировать можно без повторной выгрузки
- Дешевле compute (хранилище колоночное, JOIN'ы оптимизированы)
- Аудит проще: видишь сырьё и финал одновременно
Архитектура слоёв
sources → raw (extract+load) → staging (clean) → intermediate (joins) → marts (business)
↑ ↑ ↑ ↑
идемпотентно типы+dedup бизнес-логика для BI/аналитики
| Слой | Что внутри | Тесты | Granularity | | raw | 1:1 копия источника, history | schema only | event-level | | staging | переименование, типы, dedup | not_null, unique | event-level | | intermediate | JOIN'ы, агрегаты-полуфабрикаты | referential | mixed | | marts | dimensional model (см. Kimball) | business rules | по grain'у факта |
Extract: стратегии
| Стратегия | Когда | Подводные камни | | Full refresh | Малая таблица (<1M rows) или dim без истории | Пик нагрузки, дороже compute | | Incremental by updated_at | Есть надёжный updated_at + есть индекс | Если строка обновлена ретроактивно — пропустишь | | CDC (change data capture) | Прод-OLTP с binlog/WAL | Сложная инфра (Debezium, Fivetran), порядок событий важен | | Append-only events | Stripe-style API, immutable события | Только для immutable; для mutable нужен snapshot |
Hybrid: ежечасный incremental + еженочный full reconciliation на критичных таблицах.
Идемпотентность — обязательна
Запуск дважды должен дать тот же результат. Правила:
- В raw используй
MERGE/INSERT OVERWRITE PARTITION, неINSERT(дубли) - В staging —
{{ ref('raw') }}черезunique_key,incremental_strategy='merge'(dbt) - Partition by extract_date — переналив одного дня не трогает остальные
- Watermark: храни в helper-таблице
pipeline_state(table, last_extracted_at, rows)
Инкрементальная загрузка vs full refresh
| Решение | Когда | | Full | таблица <10M, ежедневный батч, простой dim | | Incremental | факт >100M, появляются только новые события, есть надёжный cursor | | Snapshot (SCD-2) | dim меняется и история важна |
Для incremental всегда оставляй --full-refresh команду: при изменении схемы прогоняешь весь raw в новый staging.
Freshness SLA
Из SLA "{{freshness_sla}}" выводи частоту: если marts ≤ 1 час, raw — каждые 15 минут, staging — каждые 30, marts — каждые 60. Мониторь max(loaded_at) в каждом слое, алертуй при > SLA × 1.5.
Тесты в каждом слое
- raw: schema (колонки/типы не уплыли)
- staging: not_null, unique, accepted_values
- marts: referential integrity (FK), business rules ("сумма выручки не падает >50% w/w")
Вывод
## Источники → стратегия extract
| Источник | Volume/day | Стратегия | Cursor | Cadence |
## Слои
raw.{source}__{table}
staging.stg_{source}__{table}
intermediate.int_{topic}
marts.fct_{process} / dim_{entity}
## Idempotency contract
| Слой | unique_key | merge column | partition |
## Freshness alerts
| Mart | SLA | Алерт-канал |
## Backfill plan
Команды для full refresh с разбивкой по партициям.
Anti-patterns
- ❌ Грузить в marts напрямую, минуя raw — нет источника правды для перетрансформации
- ❌ Incremental без watermark в helper-таблице — после рестарта не знаешь, с какого момента продолжить
- ❌
INSERT INTOбезMERGE/OVERWRITE— дубли при повторном запуске, метрики врут - ❌ Бизнес-логика в raw ("отфильтруем тестовых юзеров") — потеряешь возможность аналитики на полном объёме
- ❌ Один монолитный SQL на 800 строк вместо staging→int→mart — невозможно тестировать и переиспользовать
Последовательный пайплайн агентов
Когда параллель не подходит: цепочка агентов с явными контрактами между шагами.
Идемпотентность: ключи, storage, retry
Idempotency keys (UUID), Redis storage с TTL, retry strategy, edge-cases с concurrent same-key и retry после success.
Дизайн webhooks: payload, подпись, retry, идемпотентность
Полный дизайн исходящих webhooks: схема payload, HMAC-подпись, политика ретраев, идемпотентность, защита от replay, observability, dead letter, юзер-debug.