Learning Platform
Глоссарий Troubleshooting
Урок 18.01 · 22 мин
Начальный
CLIdbt rundbt builddbt testdbt debugdbt retry

dbt — это в первую очередь CLI-инструмент. Чтобы стать продуктивным, нужно знать примерно дюжину команд, понимать, какая что делает и в каком порядке их обычно дёргают. В этом уроке — карта повседневного арсенала junior analytics engineer.

Анатомия команды

Любая команда dbt имеет вид:

dbt <command> [global-options] [command-options]

Глобальные опции (работают почти везде):

  • --profiles-dir PATH — где искать profiles.yml (по умолчанию ~/.dbt/)
  • --project-dir PATH — корень проекта (по умолчанию текущая директория)
  • --target NAME — какой output из profile использовать (dev, prod, …)
  • --vars '{key: value}' — передать переменные модели
  • --quiet / --debug — уровень логирования

Поэтому полная команда часто выглядит как dbt --debug run --select stg_orders --target dev. Чем больше проект, тем чаще ты будешь использовать флаги.

14 команд, которые ты будешь использовать каждый день

Сгруппированы по фазам жизни проекта: setup, build, observe, debug.

Setupinit, debug, deps, clean
Buildseed, run, test, build, snapshot
Inspectcompile, show, list, docs
Recoverretry, run-operation, source freshness

dbt debug — первое, что запускают

Когда что-то не работает — начинай отсюда. dbt debug проверяет три вещи: что dbt видит твой проект, что profiles.yml валиден и что dbt может подключиться к warehouse.

$ dbt debug
14:23:15  Running with dbt=1.10.2
14:23:15  dbt version: 1.10.2
14:23:15  python version: 3.11.7
14:23:15  python path: /Users/me/.venv/bin/python
14:23:15  os info: macOS-14.5
14:23:15  Using profiles dir at /Users/me/.dbt
14:23:15  Using profiles.yml file at /Users/me/.dbt/profiles.yml
14:23:15  Using dbt_project.yml file at /Users/me/jaffle_shop/dbt_project.yml
14:23:15  Configuration:
14:23:15    profiles.yml file [OK found and valid]
14:23:15    dbt_project.yml file [OK found and valid]
14:23:15  Required dependencies:
14:23:15    - git [OK found]
14:23:15  Connection:
14:23:15    database: jaffle_shop
14:23:15    schema: main
14:23:15    path: ./jaffle_shop.duckdb
14:23:15    Connection test: [OK connection ok]
14:23:15  All checks passed!

Если хотя бы одна строка вместо [OK ...] показывает [ERROR ...] — фикс начни здесь, в run/test пока бесполезно лезть.

TIP

dbt debug --config-dir выводит путь к profiles.yml. Полезно, когда есть несколько активных venv и не помнишь, какой профиль сейчас активен.


dbt deps — установка пакетов

Если у тебя в проекте есть packages.yml, перед первым запуском нужно его подтянуть:

$ dbt deps
14:25:01  Installing dbt-labs/dbt_utils
14:25:02    Installed from version 1.3.0
14:25:02  Installing calogica/dbt_expectations
14:25:02    Installed from version 0.10.4

Пакеты складываются в dbt_packages/ в корне проекта. Эта папка в .gitignore — её локально регенерируешь после каждого git pull, который тронул packages.yml.

Самая частая ошибка junior: меняют packages.yml, не запускают dbt deps, потом удивляются «macro dbt_utils.star not defined».


dbt seed — CSV в таблицы

Сидирование (dbt seed) превращает CSV из папки seeds/ в таблицы в warehouse. Используется для маленьких справочников: коды стран, currency-маппинги, тестовые фикстуры.

$ dbt seed
14:27:14  Running with dbt=1.10.2
14:27:14  Found 2 seeds, 0 models, 0 tests, 0 sources, 0 exposures, 0 metrics
14:27:14
14:27:15  Concurrency: 4 threads (target='dev')
14:27:15
14:27:15  1 of 2 START seed file main.country_codes ... [RUN]
14:27:15  1 of 2 OK loaded seed file main.country_codes ... [INSERT 250 in 0.12s]
14:27:15  2 of 2 START seed file main.products_catalog ... [RUN]
14:27:15  2 of 2 OK loaded seed file main.products_catalog ... [INSERT 12 in 0.04s]
14:27:15
14:27:15  Finished running 2 seeds in 0 hours 0 minutes and 0.23 seconds (0.23s).
14:27:15  Completed successfully

dbt seed использует full-refresh по умолчанию — то есть DROP TABLE + CREATE TABLE каждый раз. Это нормально для маленьких сидов. Если у тебя сид >5 МБ — это сигнал, что данные надо было класть в source, а не в git.


dbt run — материализация моделей

dbt run берёт все модели в models/, компилирует Jinja в SQL и выполняет каждую в warehouse:

$ dbt run
14:30:42  Running with dbt=1.10.2
14:30:42  Found 8 models, 0 tests, 2 sources, 0 exposures, 0 metrics
14:30:42
14:30:42  Concurrency: 4 threads (target='dev')
14:30:42
14:30:42  1 of 8 START sql view model main.stg_jaffle__customers ... [RUN]
14:30:42  2 of 8 START sql view model main.stg_jaffle__orders ... [RUN]
14:30:42  3 of 8 START sql view model main.stg_jaffle__order_items ... [RUN]
14:30:42  4 of 8 START sql view model main.stg_jaffle__payments ... [RUN]
14:30:42  1 of 8 OK created sql view model main.stg_jaffle__customers ... [OK in 0.08s]
14:30:42  2 of 8 OK created sql view model main.stg_jaffle__orders ... [OK in 0.09s]
14:30:42  3 of 8 OK created sql view model main.stg_jaffle__order_items ... [OK in 0.08s]
14:30:42  4 of 8 OK created sql view model main.stg_jaffle__payments ... [OK in 0.07s]
14:30:42  5 of 8 START sql view model main.int_order_items_pivoted ... [RUN]
14:30:42  5 of 8 OK created sql view model main.int_order_items_pivoted ... [OK in 0.11s]
14:30:42  6 of 8 START sql table model main.customers ... [RUN]
14:30:42  7 of 8 START sql table model main.orders ... [RUN]
14:30:42  8 of 8 START sql table model main.revenue_daily ... [RUN]
14:30:42  6 of 8 OK created sql table model main.customers ... [OK in 0.32s]
14:30:42  7 of 8 OK created sql table model main.orders ... [OK in 0.41s]
14:30:42  8 of 8 OK created sql table model main.revenue_daily ... [OK in 0.28s]
14:30:43
14:30:43  Finished running 5 view models, 3 table models in 0 hours 0 minutes and 1.49 seconds (1.49s).
14:30:43  Completed successfully
14:30:43
14:30:43  Done. PASS=8 WARN=0 ERROR=0 SKIP=0 TOTAL=8

Несколько важных моментов:

  • Модели бегут параллельно до threads штук одновременно (в примере 4). Параллелизм ограничен только зависимостями DAG.
  • В одном dbt run модели бегут в порядке топологической сортировки: сначала staging, потом intermediate, потом marts.
  • Если одна модель упала — её downstream-зависимости пропускаются (SKIP), а остальные ветки DAG продолжают.

Важно: dbt run не запускает тесты. Если хочешь и материализовать, и тестировать одной командой — это dbt build.


dbt test — запуск тестов

dbt test запускает все тесты, описанные в YAML (generic) и в tests/ (singular). По дефолту тесты ничего не материализуют — они компилируются в SELECT ... FROM ... запросы, которые должны вернуть ноль строк. Если запрос вернул хоть одну — тест провалился.

$ dbt test
14:32:18  Running with dbt=1.10.2
14:32:18  Found 8 models, 14 tests, 2 sources, 0 exposures, 0 metrics
14:32:18
14:32:18  1 of 14 START test not_null_customers_customer_id ... [RUN]
14:32:18  1 of 14 PASS not_null_customers_customer_id ... [PASS in 0.04s]
14:32:18  2 of 14 START test unique_customers_customer_id ... [RUN]
14:32:18  2 of 14 PASS unique_customers_customer_id ... [PASS in 0.03s]
...
14:32:19  Finished running 14 tests in 0 hours 0 minutes and 1.12 seconds (1.12s).
14:32:19
14:32:19  Done. PASS=14 WARN=0 ERROR=0 SKIP=0 TOTAL=14

dbt test --select customers — запустит только тесты на модели customers. dbt test --select source:jaffle — только тесты на source-таблицы.


dbt build — всё за один проход

dbt build — это композит: он запускает в правильном порядке seed -> snapshot -> run -> test, с одним важным правилом: если модель упала или её upstream-тест провалился — все downstream-модели пропускаются.

$ dbt build
14:35:01  Concurrency: 4 threads (target='dev')
14:35:01
14:35:01  1 of 23 START seed file main.country_codes ... [PASS in 0.12s]
14:35:01  2 of 23 START seed file main.products_catalog ... [PASS in 0.04s]
14:35:01  3 of 23 START sql view model main.stg_jaffle__customers ... [OK in 0.08s]
14:35:01  4 of 23 START test not_null_stg_jaffle__customers_customer_id ... [PASS in 0.04s]
14:35:01  5 of 23 START sql view model main.stg_jaffle__orders ... [OK in 0.09s]
14:35:01  6 of 23 START test not_null_stg_jaffle__orders_order_id ... [FAIL in 0.05s]
14:35:01  7 of 23 SKIP relation main.customers ... [SKIP]
14:35:01  8 of 23 SKIP relation main.orders ... [SKIP]
...
14:35:01  Done. PASS=20 WARN=0 ERROR=1 SKIP=2 TOTAL=23

Заметь: stg_jaffle__orders материализовалась, но тест not_null_order_id провалился — и downstream-модели customers и orders были пропущены, не запущены вообще. Это и есть «short-circuit upstream failures», главная фича dbt build.

TIP

В production CI/CD используют только dbt build, никогда dbt run + dbt test отдельно. Причина: если между ними упасть, CI зелёный по run, но даты грязные.


dbt compile — посмотреть SQL без запуска

dbt compile берёт твои Jinja-модели, разворачивает {% %}, {{ ref() }}, {{ source() }} в готовый SQL и пишет в target/compiled/. Не запускает ничего в warehouse.

$ dbt compile --select customers
14:40:11  Found 8 models, ...
14:40:11  Concurrency: 4 threads
14:40:11
14:40:11  1 of 1 OK compiled customers ... [OK in 0.02s]
14:40:11  Finished compiling 1 model in 0.45s

Теперь в target/compiled/jaffle_shop/models/marts/customers.sql лежит готовый SQL. Это главный инструмент дебага — открываешь файл, копируешь SQL в duckdb shell или клиент warehouse, прогоняешь руками, видишь точно, что dbt отправил.


dbt show — превью без материализации

dbt show --select customers компилирует модель, оборачивает её в SELECT * FROM (...) LIMIT 5 и выполняет в warehouse, выводя результат прямо в терминал:

$ dbt show --select customers --limit 3
14:42:08  Previewing node 'customers':
| customer_id | first_name | last_name | total_spend_cents |
|-------------|------------|-----------|-------------------|
|           1 | Anna       | Petrova   |             12450 |
|           2 | Boris      | Ivanov    |              8900 |
|           3 | Carla      | Smith     |             15200 |

В отличие от dbt run, результат не материализуется в warehouse. Полезно когда хочешь быстро проверить «а что вообще модель сейчас выдаёт?», не пересоздавая таблицу.

--inline "SELECT ..." позволяет показать произвольный SQL без модели:

$ dbt show --inline "SELECT count(*) FROM {{ ref('customers') }}"

dbt list — какие узлы есть в проекте

dbt list (или dbt ls) перечисляет узлы DAG, удовлетворяющие селектору. Без аргументов — все:

$ dbt list
jaffle_shop.staging.stg_jaffle__customers
jaffle_shop.staging.stg_jaffle__orders
jaffle_shop.staging.stg_jaffle__order_items
jaffle_shop.staging.stg_jaffle__payments
jaffle_shop.intermediate.int_order_items_pivoted
jaffle_shop.marts.customers
jaffle_shop.marts.orders
jaffle_shop.marts.revenue_daily

С селектором — только подмножество:

$ dbt list --select tag:daily
jaffle_shop.marts.revenue_daily

$ dbt list --select customers+
jaffle_shop.marts.customers

Очень полезно для CI: dbt list --select state:modified+ покажет, что именно изменилось в PR.


dbt docs generate и dbt docs serve

dbt docs generate собирает target/catalog.json и target/manifest.json — это метаданные о всех моделях, колонках, типах. Затем dbt docs serve запускает локальный веб-сервер с интерактивной документацией:

$ dbt docs generate
$ dbt docs serve --port 8080
14:45:13  Serving docs at http://localhost:8080/
14:45:13  Press Ctrl+C to exit

Открывается lineage-граф со всеми связями ref() и source(). Junior часто запускают serve без generate — получают пустую страницу. Always generate first.


dbt clean — снести target

dbt clean удаляет target/ и dbt_packages/. Это нужно если:

  • что-то странное в кешированных артефактах (редко)
  • хочешь форсировать dbt deps заново
  • освободить место на диске
$ dbt clean
14:47:02  Checking target/...
14:47:02    deleted: target/
14:47:02  Checking dbt_packages/...
14:47:02    deleted: dbt_packages/

dbt source freshness — проверка свежести источников

Если у тебя в sources.yml настроены freshness: warn_after / error_after, эта команда сравнивает max(loaded_at_field) с текущим временем:

$ dbt source freshness
14:50:14  1 of 4 START freshness of jaffle.customers ... [RUN]
14:50:14  1 of 4 PASS freshness of jaffle.customers ... [PASS in 0.05s]
14:50:14  2 of 4 START freshness of jaffle.orders ... [RUN]
14:50:14  2 of 4 WARN freshness of jaffle.orders ... [WARN in 0.04s]
14:50:14  3 of 4 START freshness of jaffle.order_items ... [RUN]
14:50:14  3 of 4 ERROR STALE freshness of jaffle.order_items ... [ERROR STALE in 0.04s]

WARN означает превышен warn_after, ERROR STALE — превышен error_after. В CI после dbt source freshness обычно ставят dbt build, и если источники протухли — билд не запускают.


dbt run-operation — позвать макрос вручную

Иногда нужно выполнить кусок SQL/Jinja, не привязанный к модели. Например, GRANT SELECT после деплоя или VACUUM. Это делается через макрос + dbt run-operation:

$ dbt run-operation grant_select_on_marts --args '{role: "analyst"}'
14:53:02  Running with dbt=1.10.2
14:53:02  GRANT SELECT ON main.customers TO analyst;
14:53:02  GRANT SELECT ON main.orders TO analyst;

Макрос написан в macros/grant_select_on_marts.sql. Подробнее про макросы — в модуле 11.


dbt retry — продолжить с места падения

Если dbt build упал на 50-й из 200 моделей, не хочется запускать всё заново. dbt retry смотрит в target/run_results.json, находит все узлы со статусом error или skipped, и запускает только их + всё, что ниже по DAG:

$ dbt build
... 50 models OK, 1 ERROR (stg_orders), 149 SKIP ...

$ dbt retry
14:55:01  Retrying 150 nodes from previous run
14:55:01  1 of 150 START sql view model main.stg_orders ... [OK in 0.08s]
14:55:01  ... 149 more ...
14:55:02  Done. PASS=150 WARN=0 ERROR=0 SKIP=0 TOTAL=150

Гениально экономит время на больших проектах. Особенно при flaky-warehouse, когда падение было сетевое и не повторится.


Попробуй сам

  1. В терминале с активным dbt-проектом запусти подряд:

    dbt debug
    dbt list
    dbt list --resource-type test
    dbt compile --select customers

    Посмотри, что появилось в target/compiled/.

  2. Сломай одну staging-модель (например, добавь SELECT * FORM ... опечатка). Запусти dbt build. Посмотри в выводе, что SKIP появился на downstream. Затем исправь опечатку и запусти dbt retry.

  3. Запусти dbt docs generate && dbt docs serve --port 8080. Открой http://localhost:8080, найди lineage-граф.


Чек-лист

  • Setup: dbt debug -> dbt deps -> dbt seed.
  • Build day-to-day: dbt build --select <something>.
  • Production CI: всегда dbt build, никогда dbt run + dbt test отдельно.
  • Дебаг: dbt compile --select model_name, открой target/compiled/.../model_name.sql.
  • Превью: dbt show --select model_name --limit 10.
  • Catalog: dbt docs generate ПЕРЕД dbt docs serve.
  • Recovery: dbt retry после частичного падения.
  • Freshness: dbt source freshness в начале CI-пайплайна.
Что такое CI/CD
Проверка знанийKnowledge check
Команда в проекте запускает в CI dbt run, потом dbt test. Тест "not_null on stg_orders.order_id" падает 1 раз в неделю на проде. dbt run проходит OK, мониторинг говорит "build green", но в marts оказываются строки с NULL order_id. Почему это плохой паттерн и что использовать вместо?
ОтветAnswer
При паре dbt run + dbt test модели уже материализовались в warehouse к моменту запуска dbt test. Если тест провалился на stg_orders, customers (downstream) уже посчитался по грязным данным и записан в warehouse — потребители BI видят протекшие NULL. dbt build решает эту проблему через short-circuit: модель stg_orders материализуется, сразу запускаются её тесты, и если хоть один провалился — все downstream-модели пропускаются (SKIP). То есть customers/orders/revenue_daily не пересоздадутся, в warehouse останутся вчерашние корректные данные. Поэтому в production CI используют только dbt build. Дополнительно: store_failures=true сохраняет проваленные строки в специальную таблицу для разбора.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 6. Чем dbt build принципиально отличается от dbt run + dbt test, и почему в production CI используют именно dbt build?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4