dbt

Senior

Как сделать, чтобы CI не пересобирал весь dbt-проект при каждом запуске?

dbt по умолчанию собирает всё. State-based selection — единственное, что делает стоимость CI пропорциональной тому, что реально изменилось, а не размеру проекта.

Читать на:
Полная пересборка state:modified+ Только изменённые
Примерно как сужается область сборки CI по мере внедрения state-based selection — не измеренный бенчмарк.

Проект dbt с 40 моделями и проект с 400 выполняют одну и ту же команду dbt build — и платят за это совершенно разные счета за CI, если только что-то реально не сообщает dbt, что изменилось.

Что на самом деле сравнивает state-based selection

После каждого успешного запуска dbt записывает manifest.json, который полностью описывает скомпилированный SQL каждой модели и её место в DAG. --select state:modified+ сравнивает текущий manifest с сохранённым, на который вы указываете, и собирает только те модели, чей скомпилированный SQL реально изменился, плюс всё, что от них зависит ниже по графу.

# --state указывает на вчерашний manifest, не на сегодняшний dbt build --select state:modified+ --state ./prod-manifest

Почему это незаметно никогда не срабатывает

Самая частая причина, по которой state selection ничего не даёт: CI никогда не сохранял и не подтягивал manifest предыдущего запуска, так что --state не с чем сравнивать. dbt не выдаёт ошибку — он просто не может найти изменённые модели относительно ничего, и сборка молча делает всё подряд. Исправление — относиться к manifest.json как к артефакту CI, который загружается после каждого успешного прод-запуска и скачивается в начале следующего.

Другой незаметный убийца: привычка к full-refresh

Флаг --full-refresh, оставленный в команде CI «на всякий случай», пересобирает каждую инкрементальную модель с нуля независимо от того, что нашла state selection — для этих моделей diff полностью игнорируется. Обычно это остаток от отладки одной сломанной инкрементальной модели, который так и не убрали. Оставьте --full-refresh для явного, осознанного запуска по одной модели, а не для постоянного флага CI.

Признак: если время сборки CI зависит от размера всего проекта, а не от размера diff в PR — state selection на самом деле не работает: сначала проверьте отсутствующий manifest в --state, затем — затесавшийся --full-refresh.

Посмотрите, как это выглядит на вашем собственном пайплайне.

Загрузите реальный event log Spark, run_results.json от dbt или экспорт метрик Flink — и получите конкретные рекомендации, которые нужно одобрить, а не ещё одно эмпирическое правило.