defer 是一种强大的功能,可以在沙盒环境中运行部分模型、测试或函数,而无需先构建其上游父节点。函数相关能力适用于 dbt v1.11 及以后版本。在大型项目中只测试少量模型时,这能节省时间和计算资源。 sandbox environment
Use ‘defer’ to modify end-of-pipeline models by pointing to production models, instead of running everything upstream.defer 需要前一次 dbt 调用生成的 manifest。通过 –state 参数提供其路径,或设置 DBT_ENGINE_STATE 环境变量(适用于 dbt v1.11 及以后版本)。配合 state: 选择方法,这些功能可以实现 Slim CI。详见状态文档。 state
某些场景也可以用 dbt clone 实现类似能力。详情见 clone 文档。 clone
可以为 state:modified 与 –defer 使用不同状态:分别通过 –state / DBT_ENGINE_STATE 和 –defer-state / DBT_ENGINE_DEFER_STATE 指定不同 manifest 路径。两个 DBT_ENGINE_* 环境变量适用于 dbt v1.11 及以后版本。这可以更细致地控制以下需求:
- 与某个环境或过去某个时间点的逻辑状态进行比较。
- 将引用转向另一个环境或时间点已经应用的状态。
未指定 –defer-state 时,defer 使用 –state 提供的 manifest。多数情况下,两者应该使用相同状态:与生产环境比较逻辑改动,并让尚未构建的上游资源引用回退到生产环境。
用法
dbt run --select [...] --defer --state path/to/artifacts
dbt test --select [...] --defer --state path/to/artifacts
默认情况下,dbt 使用目标命名空间解析 ref 调用。 target
启用 –defer 后,dbt 可以改用状态 manifest 解析 ref 以及 function 调用(函数相关行为适用于 dbt v1.11 及以后版本),但必须满足:
- 节点不属于已选择的节点;并且
- 该节点在数据库中不存在,或者使用了 –favor-state。
临时模型(ephemeral)永远不会 defer,因为它们是其他 ref 调用的透传节点。
(Applies to dbt v1.11 and later)
通过 {{ function('...') }} 引用的用户定义函数(UDF)也按相同条件 defer。发生 defer 时,若 UDF 未被选择或未在当前目标中构建,function() 会解析为状态 manifest 中的函数定义。 User-defined functions (UDFs)
使用 defer 时,查询可能读取生产数据、开发数据,或混合读取两者。这可能产生意料之外的结果:
- 如果开发环境设置了数据范围限制,而生产环境没有,读取的数据可能多于预期。
- 依赖多个父节点的测试,例如 relationships,可能跨环境运行。
defer 要求同时设置 –defer 和 –state,可以显式传入参数,也可以设置环境变量 DBT_ENGINE_DEFER 和 DBT_ENGINE_STATE(适用于 dbt v1.11 及以后版本)。更多信息见持续集成文档。 Continuous integration
优先使用状态
传入 –favor-state 时,dbt 优先使用 –state 目录中的节点定义。但如果节点本身也在已选择节点中,这条规则不适用。
示例
在本地开发环境中,所有模型创建在目标 schema dev_alice 中;生产环境中的同样模型则创建在 prod schema 中。
取得生产运行生成的 dbt 产物,即 manifest.json,并将它复制到本地 prod-run-artifacts 目录。 artifacts
运行模型(run)
假设正在修改 model_b:
select
id,
count(*)
from {{ ref('model_a') }}
group by 1
现在要测试改动,但开发 schema dev_alice 中没有任何对象。
- 常规运行
- 使用 defer 运行
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 是否存在。如果不存在,所有 {{ 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。之前的 dbt run 没有构建 model_a,因此 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 是否存在。如果不存在,包括 schema 测试在内的所有 {{ ref('model_a') }} 都会改为使用 prod.model_a,查询就能成功。不过,是否真的应该跨环境测试引用完整性,是另一个需要考虑的问题。
相关文档
- 在 dbt 中使用 defer Using defer in dbt
- on_configuration_change











暂无评论内容