Learning Platform
Глоссарий Troubleshooting
Урок 09.03 · 14 мин
Начальный
severityerrorwarnerror_ifwarn_ifpipeline blocking

В прошлом уроке мы разобрали, какие тесты вешать где. Теперь — как контролировать поведение при провале. Не каждый failed test одинаково критичен. Один обязательно блокирует production-deploy, другой — просто шум, на который стоит обратить внимание.

Этим управляет параметр severity.

Базовая идея

Каждый тест в dbt может быть одного из двух уровней:

  • error (default) — провал блокирует pipeline. Exit code != 0, downstream не выполняется в dbt build. CI-job падает.
  • warn — провал показывается как warning, но не останавливает. Exit code остаётся 0.
severity: error vs warn

error — твёрдая граница, провал блокирует. warn — мягкое предупреждение, информирует, но не останавливает. Выбор зависит от того, насколько критичен инвариант.

severity: error (default)failed test = exit 1
dbt build останавливаетсяdownstream skip
severity: warnfailed test = exit 0
dbt build продолжаетсяwarning в логах

Синтаксис

В YAML severity указывается через config:

models:
  - name: fact_orders
    columns:
      - name: status
        tests:
          - accepted_values:
              values: ['pending', 'shipped', 'delivered', 'cancelled']
              config:
                severity: warn

Или явно error (это default):

- name: customer_id
  tests:
    - not_null:
        config:
          severity: error

Когда severity: warn

Несколько типичных сценариев:

1. Новые enum-значения. Если в status может появиться новое значение (например, добавили ‘refunded’), и ты пока не уверен, что добавлять — severity: warn позволяет узнать о появлении нового, не блокируя pipeline.

2. Постепенная миграция. Когда добавляешь тест, который сейчас провалится из-за legacy данных. Сначала severity: warn — фикси и постепенно убирай нарушители. Потом меняешь на error.

3. Soft business rules. “Хорошо бы amount был > 0, но иногда returns/refunds могут давать отрицательные”. Warn информирует, error блокирует ошибочно.

4. Информационные проверки. “Проверь, что customer_segment не NULL” — было бы хорошо, но не критично для downstream.

Когда severity: error (всегда default)

  • PK тесты — not_null + unique. Если они провалились, у тебя сломанные данные.
  • Foreign keys — relationships. Сломанный FK ломает JOIN в дашбордах.
  • Критичные NOT NULL — без email клиента уведомления не отправятся.

error_if и warn_if: условные пороги

dbt позволяет настроить порог нарушителей, при котором тест меняет severity. По умолчанию любая строка-нарушитель = FAIL. С error_if можно сказать: “если меньше 100 нарушителей — WARN, если больше 100 — ERROR”.

Синтаксис:

- name: status
  tests:
    - accepted_values:
        values: ['pending', 'shipped', 'delivered', 'cancelled']
        config:
          severity: warn
          error_if: ">100"      # ERROR если 100+ нарушителей
          warn_if: ">0"         # WARN если хоть один (это default для warn)

Логика порогов:

  1. dbt выполняет тест -> получает count нарушителей.
  2. Если count > error_if -> ERROR.
  3. Иначе если count > warn_if -> WARN.
  4. Иначе -> PASS.
TIP

error_if/warn_if полезны для тестов на больших таблицах, где небольшое количество нарушителей — terpимо (например, race condition в loader даёт 1-2 строки с invalid status). Жёсткий error на каждое сломанное событие — overkill. Threshold позволяет получать алерт только при больших проблемах.

Конкретный пример порогов

Сценарий: у тебя fact_events с миллиардом строк. Иногда (1-2 раза в сутки) loader даёт строку с UNKNOWN status. Не критично, но хочется мониторить тренд.

- name: status
  tests:
    - accepted_values:
        values: ['login', 'logout', 'click', 'view']
        config:
          severity: error
          error_if: ">1000"   # критично, если 1000+
          warn_if: ">10"      # info, если 10+

Поведение:

  • 0-10 нарушителей: PASS — нормальная погрешность.
  • 11-1000: WARN — обрати внимание, но pipeline идёт.
  • 1000+: ERROR — что-то серьёзное сломалось, останавливаем.

Severity в зависимости от среды

Иногда нужно жёстче в prod, мягче в dev. dbt поддерживает условные конфиги через target:

- name: customer_id
  tests:
    - relationships:
        to: ref('dim_customers')
        field: customer_id
        config:
          severity: '{{ "error" if target.name == "prod" else "warn" }}'

В prod — error (блокирует). В dev — warn (не блокирует, чтобы не мешать разработке).

Реализуется через target.name и Jinja-условие. Подробнее в модуле про variables/env.

Output dbt test с разной severity

Когда запустишь dbt test, увидишь разные статусы:

14:23:01  1 of 4 START test not_null_fact_orders_order_id ........ [RUN]
14:23:01  2 of 4 START test unique_fact_orders_order_id .......... [RUN]
14:23:01  3 of 4 START test relationships_fact_orders_customer_id . [RUN]
14:23:01  4 of 4 START test accepted_values_fact_orders_status ... [RUN]

14:23:02  1 of 4 PASS not_null_fact_orders_order_id ............... [PASS in 0.18s]
14:23:02  2 of 4 PASS unique_fact_orders_order_id ................. [PASS in 0.22s]
14:23:02  3 of 4 WARN 3 relationships_fact_orders_customer_id ..... [WARN 3 in 0.31s]
14:23:02  4 of 4 FAIL 1 accepted_values_fact_orders_status ........ [FAIL 1 in 0.18s]

Done. PASS=2 WARN=1 ERROR=1 SKIP=0 TOTAL=4
  • PASS — тест прошёл (0 нарушителей).
  • WARN 3 — тест warn-уровня нашёл 3 нарушителя, pipeline продолжается.
  • FAIL 1 — тест error-уровня нашёл 1 нарушителя, pipeline останавливается.

Exit code: при наличии хотя бы одного FAIL = 1. При наличии только WARN = 0.

Severity на уровне модели/проекта

Можно задать дефолтный severity для всего проекта через dbt_project.yml:

tests:
  +severity: warn

Или для тестов конкретной модели:

models:
  - name: experimental_mart
    config:
      tests:
        +severity: warn

Используется реже, но полезно когда есть экспериментальная модель и не хочешь, чтобы её тесты блокировали main pipeline.

Custom thresholds через config

В dbt-core 1.6+ можно использовать warn_after и error_after (не путать с freshness). Синтаксис:

- name: status
  tests:
    - accepted_values:
        values: ['active', 'inactive']
        config:
          warn_if: ">1"
          error_if: ">100"

Это equivalent error_if/warn_if. Главное — string-comparison, чтобы dbt мог парсить число.

Best practices

1. По умолчанию — error. На PK, FK, критичных NOT NULL всегда error.

2. Warn для миграционных моментов. Когда внедряешь новый тест на legacy данных — сначала warn, постепенно фикси и переключай на error.

3. Threshold для шумных таблиц. На больших fact-таблицах с миллиардом событий и редкими аномалиями — error_if: “>N”.

4. Не злоупотребляй warn. “Все тесты warn, чтобы не мешали разработке” — это плохо. Через месяц никто не смотрит на warnings, и реальные проблемы прячутся.

5. Reviewing warns. В каждом sprint смотри dbt test output, разбирайся с warnings. Либо фикси, либо упрощай тест.

DuckDB-специфика для severity

Severity никак не зависит от warehouse — это чисто dbt-механика. На DuckDB работает так же, как везде. Exit code от dbt test одинаков на любом адаптере.

Команды для проверки severity

# Список тестов с конфигом
dbt list --resource-type test --output json | jq '.[] | {name, config: .config}'

# Тесты только определённого severity
dbt test --select "config.severity:warn"

Попробуй сам

Возьми существующий тест, например на accepted_values:

- name: status
  tests:
    - accepted_values:
        values: ['pending', 'shipped']

Запусти dbt test — если data содержит ‘cancelled’, тест FAIL’нет.

Добавь severity: warn:

- name: status
  tests:
    - accepted_values:
        values: ['pending', 'shipped']
        config:
          severity: warn

Снова dbt test. Теперь будет WARN, exit code 0.

Затем добавь threshold:

- name: status
  tests:
    - accepted_values:
        values: ['pending', 'shipped']
        config:
          severity: error
          warn_if: ">0"
          error_if: ">10"

Если нарушителей 5 — WARN. Если 15 — ERROR. Проверь, добавив тестовые данные.

Что мы поняли

severity управляет поведением проваленного теста: error (default) — блокирует pipeline, exit 1; warn — предупреждение, не блокирует. error_if/warn_if задают пороги, при которых severity меняется (например, error только если 100+ нарушителей). Использовать warn для миграционных моментов, мягких правил, информационных проверок. По умолчанию — error на критичных тестах. Не злоупотреблять warn, иначе они станут невидимыми.

В следующем уроке разберём store_failures — как dbt сохраняет failed строки для дебага.

Severity и thresholds: продвинутые настройки
Проверка знанийKnowledge check
У тебя fact_events с миллиардом строк. На колонке status повешен accepted_values тест. Иногда из-за race condition в loader 1-2 строки с UNKNOWN status. Тест каждое утро FAIL'ит, блокируя deploy. Что настроить?
ОтветAnswer
Использовать error_if/warn_if пороги. Например: severity: error, warn_if: '>0', error_if: '>50'. Тогда 1-2 нарушителя дают WARN (информируют, не блокируют), а 50+ — ERROR (реальная проблема, блокировать). Альтернатива — починить loader, чтобы race condition не давал invalid status. Но threshold-подход прагматичен: small noise — ок, big breakage — стоп.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Severity по умолчанию для теста, у которого не указан config — какой?

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

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

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

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