Skip to content

Commit 5201abf

Browse files
authored
Fix: Make sure backfill is not triggered for models downstream of met…adata changes (#4237)
1 parent 21a3a20 commit 5201abf

5 files changed

Lines changed: 39 additions & 5 deletions

File tree

sqlmesh/core/plan/builder.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,9 @@ def _categorize_snapshot(
590590
if p_id in snapshot.parents:
591591
direct_parent_categories.add(parent.change_category)
592592

593-
if not direct_parent_categories or direct_parent_categories.intersection(
593+
if snapshot.is_model and snapshot.model.forward_only:
594+
snapshot.categorize_as(SnapshotChangeCategory.FORWARD_ONLY)
595+
elif not direct_parent_categories or direct_parent_categories.intersection(
594596
{SnapshotChangeCategory.BREAKING, SnapshotChangeCategory.INDIRECT_BREAKING}
595597
):
596598
snapshot.categorize_as(SnapshotChangeCategory.INDIRECT_BREAKING)
@@ -619,6 +621,10 @@ def _apply_effective_from(self) -> None:
619621
snapshot.effective_from = self._effective_from
620622

621623
def _is_forward_only_change(self, s_id: SnapshotId) -> bool:
624+
if not self._context_diff.directly_modified(
625+
s_id.name
626+
) and not self._context_diff.indirectly_modified(s_id.name):
627+
return False
622628
snapshot = self._context_diff.snapshots[s_id]
623629
if snapshot.name in self._context_diff.modified_snapshots:
624630
_, old = self._context_diff.modified_snapshots[snapshot.name]

sqlmesh/core/snapshot/definition.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,9 @@ def create(
14731473
)
14741474
if this_deployable:
14751475
snapshot = snapshots[node]
1476-
is_forward_only_model = snapshot.is_model and snapshot.model.forward_only
1476+
is_forward_only_model = (
1477+
snapshot.is_model and snapshot.model.forward_only and not snapshot.is_metadata
1478+
)
14771479
has_auto_restatement = (
14781480
snapshot.is_model and snapshot.model.auto_restatement_cron is not None
14791481
)

tests/core/test_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def test_render_sql_model(sushi_context, assert_exp_eq, copy_to_temp_path: t.Cal
197197
def test_render_non_deployable_parent(sushi_context, assert_exp_eq, copy_to_temp_path: t.Callable):
198198
model = sushi_context.get_model("sushi.waiter_revenue_by_day")
199199
forward_only_kind = model.kind.copy(update={"forward_only": True})
200-
model = model.copy(update={"kind": forward_only_kind})
200+
model = model.copy(update={"kind": forward_only_kind, "stamp": "trigger forward-only change"})
201201
sushi_context.upsert_model(model)
202202
sushi_context.plan("dev", no_prompts=True, auto_apply=True)
203203

tests/core/test_integration.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,32 @@ def test_plan_repairs_unrenderable_snapshot_state(
40494049
assert target_snapshot_in_state.model.render_query_or_raise()
40504050

40514051

4052+
@time_machine.travel("2023-01-08 15:00:00 UTC")
4053+
def test_no_backfill_for_model_downstream_of_metadata_change(init_and_plan_context: t.Callable):
4054+
context, _ = init_and_plan_context("examples/sushi")
4055+
4056+
# Make sushi.waiter_revenue_by_day a forward-only model.
4057+
forward_only_model = context.get_model("sushi.waiter_revenue_by_day")
4058+
updated_model_kind = forward_only_model.kind.copy(update={"forward_only": True})
4059+
forward_only_model = forward_only_model.copy(update={"kind": updated_model_kind})
4060+
context.upsert_model(forward_only_model)
4061+
4062+
context.plan("prod", auto_apply=True, no_prompts=True, skip_tests=True)
4063+
4064+
# Make a metadata change upstream of the forward-only model.
4065+
context.upsert_model("sushi.orders", owner="new_owner")
4066+
4067+
plan = context.plan_builder("test_dev").build()
4068+
assert plan.has_changes
4069+
assert not plan.directly_modified
4070+
assert not plan.indirectly_modified
4071+
assert not plan.missing_intervals
4072+
assert all(
4073+
snapshot.change_category == SnapshotChangeCategory.METADATA
4074+
for snapshot in plan.new_snapshots
4075+
)
4076+
4077+
40524078
@time_machine.travel("2023-01-08 15:00:00 UTC")
40534079
def test_dbt_requirements(sushi_dbt_context: Context):
40544080
assert set(sushi_dbt_context.requirements) == {"dbt-core", "dbt-duckdb"}

tests/core/test_snapshot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,11 +2034,11 @@ def test_deployability_index_categorized_forward_only_model(make_snapshot):
20342034

20352035
snapshot_a = make_snapshot(model_a)
20362036
snapshot_a.previous_versions = snapshot_a_old.all_versions
2037-
snapshot_a.categorize_as(SnapshotChangeCategory.METADATA)
2037+
snapshot_a.categorize_as(SnapshotChangeCategory.FORWARD_ONLY)
20382038

20392039
snapshot_b = make_snapshot(SqlModel(name="b", query=parse_one("SELECT 1")))
20402040
snapshot_b.parents = (snapshot_a.snapshot_id,)
2041-
snapshot_b.categorize_as(SnapshotChangeCategory.METADATA)
2041+
snapshot_b.categorize_as(SnapshotChangeCategory.FORWARD_ONLY)
20422042

20432043
deployability_index = DeployabilityIndex.create(
20442044
{s.snapshot_id: s for s in [snapshot_a, snapshot_b]}

0 commit comments

Comments
 (0)