지연
지연 (Defer)
defer는 샌드박스 환경에서 업스트림 부모를 먼저 빌드하지 않고도 프로젝트의 일부 모델/테스트/함수를 실행할 수 있게 해주는 강력한 기능이에요. 프로덕션 매니페스트를 참조해 아직 빌드되지 않은 상위 리소스를 해석하는 방법을 다뤄요.
출처: 문서
본문
defer는 샌드박스 환경에서 (dbt v1.11 이상) 모델, 테스트, 함수의 일부를 업스트림 부모를 먼저 빌드하지 않고 실행할 수 있게 해주는 강력한 기능이에요. 이는 대규모 프로젝트에서 소수의 모델을 테스트할 때 시간과 컴퓨팅 리소스를 절약할 수 있어요.
파이프라인 끝의 모델을 수정할 때 모든 것을 업스트림에서 실행하는 대신 프로덕션 모델을 가리켜 수정하려면 'defer'를 사용해요.
defer는 이전 dbt 실행의 매니페스트를 필요로 해요. --state 플래그로 경로를 제공하거나 (dbt v1.11 이상) DBT_ENGINE_STATE 환경 변수를 설정해요. state: 선택 방법과 함께 이 기능들은 "Slim CI"를 가능하게 해요. state에 대해 더 읽어 보세요.
일부 사용 사례에서는 dbt clone을 사용해 비슷한 기능을 달성할 수 있어요. 자세한 내용은 clone을 참고하세요.
state:modified와 --defer에 별도의 state를 사용하는 것이 가능해요. --state/(dbt v1.11 이상) DBT_ENGINE_STATE와 --defer-state/(dbt v1.11 이상) DBT_ENGINE_DEFER_STATE에 각각 다른 매니페스트 경로를 전달하면 되요. 이렇게 하면 다음을 원할 때 더 세밀한 제어가 가능해요:
- 한 환경이나 과거 시점의 논리적 상태와 비교
- 다른 환경이나 시점의 적용된 상태로 defer
--defer-state가 지정되지 않으면 deferral은 --state에 제공된 매니페스트를 사용해요. 대부분의 경우 두 곳에 같은 state를 사용하기를 원할 거예요. 프로덕션과 논리적 변경을 비교하고, 빌드되지 않은 업스트림 리소스에 대해서는 프로덕션 환경으로 "페일오버"하세요.
사용법 (Usage)
dbt run --select [...] --defer --state path/to/artifacts
dbt test --select [...] --defer --state path/to/artifacts
기본적으로 dbt는 ref 호출을 해석할 때 target 네임스페이스를 사용해요.
--defer가 활성화되면 dbt는 (dbt v1.11 이상) ref와 function 호출을 state 매니페스트를 사용해 해석하는데, 다음 조건일 때만 그래요:
- 해당 노드가 선택된 노드에 포함되지 않고,
- 데이터베이스에 존재하지 않거나(또는
--favor-state가 사용된 경우).
임시(ephemeral) 모델은 다른 ref 호출의 "패스스루" 역할을 하므로 절대 defer되지 않아요.
(dbt v1.11 이상) {{ function('...') }}으로 참조되는 사용자 정의 함수(UDF)는 같은 조건에서 defer돼요. defer될 때 function()은 UDF가 현재 target에서 선택되지 않았거나 빌드되지 않았다면 state 매니페스트의 함수 정의로 해석돼요.
info — defer를 사용할 때 프로덕션 데이터셋, 개발 데이터셋, 또는 둘의 혼합에서 선택할 수 있어요. 이는 예기치 않은 결과를 낼 수 있음을 유의해요:
- 개발에는 환경별 제한을 적용하는데 프로덕션에는 적용하지 않으면 예상보다 많은 데이터를 선택할 수 있어요.
- 여러 부모에 의존하는 테스트(예: relationships)가 환경을 가로질러 실행될 수 있어요.
deferral은 --defer와 --state를 모두 설정해야 해요. 플래그를 명시적으로 전달하거나 환경 변수((dbt v1.11 이상) DBT_ENGINE_DEFER와 DBT_ENGINE_STATE)를 설정해요. Continuous integration에 대한 자세한 내용은 해당 문서를 참고하세요.
상태 선호 (Favor state)
--favor-state가 전달되면 dbt는 --state 디렉터리의 노드 정의를 우선시해요. 그러나 노드가 선택된 노드에도 포함된다면 이는 적용되지 않아요.
예시 (Example)
로컬 개발 환경에서 모든 모델을 target 스키마인 dev_alice에 만들어요. 프로덕션에서는 같은 모델이 prod라는 스키마에 만들어져요.
프로덕션 실행에서 dbt가 생성한 아티팩트(특히 manifest.json)에 접근해서 prod-run-artifacts라는 로컬 디렉터리에 복사해요.
run
model_b 작업 중이에요:
models/model_b.sql
select
id,
count(*)
from {{ ref('model_a') }}
group by 1
변경 사항을 테스트하고 싶어요. 개발 스키마 dev_alice에는 아무것도 없어요.
Standard run
dbt run --select "model_b"
target/run/my_project/model_b.sql
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는 존재하지 않아 데이터베이스 오류가 발생해요.
Deferred run
dbt run --select "model_b" --defer --state prod-run-artifacts
target/run/my_project/model_b.sql
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
또한 model_a와 model_b 사이의 참조 무결성을 확립하는 relationships 테스트가 있어요:
models/resources.yml
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에서 오기 때문이에요)
Without defer
dbt test --select "model_b"
target/compiled/.../relationships_model_b_id__id__ref_model_a_.sql
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가 존재하지 않고 이 테스트 쿼리는 실패해요.
With defer
dbt test --select "model_b" --defer --state prod-run-artifacts
target/compiled/.../relationships_model_b_id__id__ref_model_a_.sql
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는 schema 테스트에 있는 것을 포함해 {{ ref('model_a') }}의 모든 인스턴스를 prod.model_a를 사용하도록 해석해요. 쿼리는 성공해요. 환경을 가로질러 참조 무결성을 실제로 테스트하고 싶은지는 다른 문제예요.
더 알아보기 (Learn more)
- Using defer in dbt — defer 사용 가이드
- on_configuration_change — 구성 변경 처리