Defer
Defer — это мощная функция, которая позволяет запускать подмножество (Версии dbt: v1.11 и новее) моделей, тестов или функций в изолированной среде без предварительного построения их вышестоящих родительских моделей. Это позволяет сэкономить время и вычислительные ресурсы, когда нужно протестировать небольшое количество моделей в большом проекте.
Используйте 'defer', чтобы модифицировать модели в конце конвейера, указывая на производственные модели, вместо того чтобы запускать все вышестоящие.Для использования defer требуется манифест из предыдущего вызова dbt. Укажите путь с помощью флага --state или переменной окружения (Версии dbt: v1.11 и новее) DBT_ENGINE_STATE. Вместе с методом выбора state: эти возможности позволяют реализовать «Slim CI». Подробнее читайте в разделе Состояние.
В некоторых случаях для достижения аналогичного результата можно использовать dbt clone. Подробнее см. в разделе clone.
Для state:modified и --defer можно использовать разные состояния, передав пути к разным манифестам через --state/(Версии dbt: v1.11 и новее) DBT_ENGINE_STATE и --defer-state/(Версии dbt: v1.11 и новее) DBT_ENGINE_DEFER_STATE соответственно. Это обеспечивает более точный контроль в случаях, когда вы хотите:
- Сравнивать с логическим состоянием из одной среды или на определённый момент в прошлом
- Использовать для defer применённое состояние из другой среды или на другой момент времени
Если --defer-state не указан, для defer будет использоваться манифест, переданный через --state. В большинстве случаев для обеих задач стоит использовать одно и то же состояние: сравнивать логические изменения с производственной средой, а для невыполненных вышестоящих ресурсов также «переключаться» на производственную среду.
Использование
dbt run --select [...] --defer --state path/to/artifacts
dbt test --select [...] --defer --state path/to/artifacts
По умолчанию dbt использует пространство имён target для разрешения вызовов ref.
Когда включён --defer, dbt разрешает вызовы ref(Версии dbt: v1.11 и новее) и function, используя манифест состояния, но только если:
- Узел не входит в число выбранных узлов, и
- Он не существует в базе данных (или используется
--favor-state).
Эфемерные модели никогда не используют отложенное разрешение ссылок, поскольку они служат «промежуточными звеньями» для других вызовов ref.
Для пользовательских функций (UDF), на которые ссылаются с помощью {{ function('...') }}, отложенное разрешение ссылок применяется при тех же условиях. В этом случае function() разрешается в определение функции из манифеста состояния, если UDF не выбрана или не создана в текущем целевом окружении.
При использовании отложенного разрешения ссылок вы можете выбирать данные из наборов данных рабочего окружения, окружения разработки или из обоих одновременно. Обратите внимание, что это может привести к неожиданным результатам:
- Если в окружении разработки вы применяете ограничения, зависящие от окружения, а в рабочем окружении — нет, вы можете выбрать больше данных, чем ожидалось.
- Тесты, зависящие от нескольких родительских объектов (например,
relationships), могут выполняться с использованием данных из разных окружений.
Для отложенного разрешения ссылок необходимо задать и --defer, и --state: либо явно передав флаги, либо установив переменные окружения ((Версии dbt: v1.11 и новее) DBT_ENGINE_DEFER и DBT_ENGINE_STATE). Подробнее см. в разделе Непрерывная интеграция.
Предпочтение состояния
Когда передан --favor-state, dbt отдает приоритет определениям узлов из каталога --state. Однако это не применяется, если узел также является частью выбранных узлов.
При передаче --favor-state dbt отдает приоритет определениям узлов из каталога --state. Однако это не применяется, если узел также входит в число выбранных узлов.
В моей локальной среде разработки я создаю все модели в моей целевой схеме, dev_alice. В производстве те же модели создаются в схеме с именем prod.
Я получаю доступ к dbt-сгенерированным артефактам (а именно manifest.json) из производственного запуска и копирую их в локальный каталог под названием prod-run-artifacts.
run
Я работал над model_b:
select
id,
count(*)
from {{ ref('model_a') }}
group by 1
Я хочу протестировать свои изменения. В моей схеме разработки, dev_alice, ничего не существует.
- Стандартный запуск
- Отложенный запуск
dbt run --select "model_b"
create or replace view dev_me.model_b as (
select
id,
count(*)
from dev_alice.model_a
group by 1
)
Если я ранее не запускал model_a в этой среде разработки, dev_alice.model_a не будет существовать, что вызовет ошибку базы данных.
dbt run --select "model_b" --defer --state prod-run-artifacts
create or replace view dev_me.model_b as (
select
id,
count(*)
from prod.model_a
group by 1
)
Поскольку model_a не выбран, dbt проверит, существует ли dev_alice.model_a. Если он не существует, dbt разрешит все экземпляры {{ ref('model_a') }} в prod.model_a.
test
У меня также есть тест relationships, который устанавливает ссылочную целостность между model_a и model_b:
models:
- name: model_b
columns:
- name: id
data_tests:
- relationships:
arguments: # available in v1.10.5 and higher. Older versions can set the <argument_name> as the top-level property.
to: ref('model_a')
field: id
(Это упрощённый пример, поскольку все данные в model_b уже поступают из model_a)
- Без defer
- С defer
dbt test --select "model_b"
select count(*) as validation_errors
from (
select id as id from dev_alice.model_b
) as child
left join (
select id as id from dev_alice.model_a
) as parent on parent.id = child.id
where child.id is not null
and parent.id is null
Тест relationships требует как model_a, так и model_b. Поскольку я не построил model_a в своем предыдущем dbt run, dev_alice.model_a не существует, и этот тестовый запрос не выполняется.
dbt test --select "model_b" --defer --state prod-run-artifacts
select count(*) as validation_errors
from (
select id as id from dev_alice.model_b
) as child
left join (
select id as id from prod.model_a
) as parent on parent.id = child.id
where child.id is not null
and parent.id is null
dbt проверит, существует ли dev_alice.model_a. Если он не существует, dbt разрешит все экземпляры {{ ref('model_a') }}, включая те, что в тестах схемы, использовать prod.model_a. Запрос выполняется успешно. Вопрос, действительно ли я хочу тестировать ссылочную целостность между средами, остается открытым.