Skip to content

Commit efa54fa

Browse files
committed
don't constrain backfill models to preview start
Signed-off-by: Jesse Hodges <hodges.jesse@gmail.com>
1 parent 7043ed7 commit efa54fa

2 files changed

Lines changed: 137 additions & 4 deletions

File tree

sqlmesh/core/plan/builder.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,17 @@ def __init__(
185185
self._explain = explain
186186

187187
self._start = start
188-
if not self._start and (
189-
self._forward_only_preview_needed or self._non_forward_only_preview_needed
190-
):
191-
self._start = default_start or yesterday_ds()
188+
if not self._start and self._forward_only_preview_needed:
189+
self._preview_start = self._preview_start or default_start or yesterday_ds()
190+
# Only use the preview fallback as the plan start for implicit selection.
191+
# None means no explicit selection; an empty set intentionally backfills no models.
192+
if self._backfill_models is None:
193+
self._start = self._preview_start
194+
195+
if not self._start and self._non_forward_only_preview_needed:
196+
# Do not bind explicit non-preview backfills to the short preview range.
197+
if self._backfill_models is None:
198+
self._start = default_start or yesterday_ds()
192199

193200
self._plan_id: str = random_id()
194201
self._model_fqn_to_snapshot = {s.name: s for s in self._context_diff.snapshots.values()}

tests/core/test_plan.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2986,6 +2986,132 @@ def _make_forward_only_preview_context_diff(make_snapshot):
29862986
return context_diff, new_snapshot
29872987

29882988

2989+
def test_forward_only_preview_start_does_not_limit_regular_backfill(make_snapshot):
2990+
context_diff, preview_new_snapshot = _make_forward_only_preview_context_diff(make_snapshot)
2991+
2992+
normal_old_snapshot = make_snapshot(
2993+
SqlModel(
2994+
name="normal",
2995+
query=parse_one("select 1, ds"),
2996+
dialect="duckdb",
2997+
kind=dict(name=ModelKindName.INCREMENTAL_BY_TIME_RANGE, time_column="ds"),
2998+
start="2025-01-01",
2999+
)
3000+
)
3001+
normal_old_snapshot.categorize_as(SnapshotChangeCategory.BREAKING)
3002+
normal_new_snapshot = make_snapshot(
3003+
SqlModel(
3004+
name="normal",
3005+
query=parse_one("select 2, ds"),
3006+
dialect="duckdb",
3007+
kind=dict(name=ModelKindName.INCREMENTAL_BY_TIME_RANGE, time_column="ds"),
3008+
start="2025-01-01",
3009+
)
3010+
)
3011+
normal_new_snapshot.previous_versions = normal_old_snapshot.all_versions
3012+
3013+
context_diff.modified_snapshots[normal_new_snapshot.name] = (
3014+
normal_new_snapshot,
3015+
normal_old_snapshot,
3016+
)
3017+
context_diff.snapshots[normal_new_snapshot.snapshot_id] = normal_new_snapshot
3018+
context_diff.new_snapshots[normal_new_snapshot.snapshot_id] = normal_new_snapshot
3019+
3020+
plan = PlanBuilder(
3021+
context_diff,
3022+
default_start="2025-01-02",
3023+
end="2025-01-03",
3024+
preview_min_intervals=1,
3025+
backfill_models={normal_new_snapshot.name, preview_new_snapshot.name},
3026+
is_dev=True,
3027+
enable_preview=True,
3028+
).build()
3029+
3030+
missing_intervals = {i.snapshot_id: i.intervals for i in plan.missing_intervals}
3031+
3032+
assert plan.provided_start is None
3033+
assert missing_intervals == {
3034+
normal_new_snapshot.snapshot_id: [
3035+
(to_timestamp("2025-01-01"), to_timestamp("2025-01-02")),
3036+
(to_timestamp("2025-01-02"), to_timestamp("2025-01-03")),
3037+
(to_timestamp("2025-01-03"), to_timestamp("2025-01-04")),
3038+
],
3039+
preview_new_snapshot.snapshot_id: [
3040+
(to_timestamp("2025-01-02"), to_timestamp("2025-01-03")),
3041+
(to_timestamp("2025-01-03"), to_timestamp("2025-01-04")),
3042+
],
3043+
}
3044+
3045+
3046+
def test_non_forward_only_preview_start_does_not_limit_explicit_backfill(make_snapshot):
3047+
old_snapshot = make_snapshot(
3048+
SqlModel(
3049+
name="a",
3050+
query=parse_one("select 1, ds"),
3051+
dialect="duckdb",
3052+
kind=IncrementalByTimeRangeKind(
3053+
time_column="ds",
3054+
auto_restatement_cron="@daily",
3055+
),
3056+
start="2025-01-01",
3057+
)
3058+
)
3059+
old_snapshot.categorize_as(SnapshotChangeCategory.BREAKING)
3060+
new_snapshot = make_snapshot(
3061+
SqlModel(
3062+
name="a",
3063+
query=parse_one("select 2, ds"),
3064+
dialect="duckdb",
3065+
kind=IncrementalByTimeRangeKind(
3066+
time_column="ds",
3067+
auto_restatement_cron="@daily",
3068+
),
3069+
start="2025-01-01",
3070+
)
3071+
)
3072+
new_snapshot.previous_versions = old_snapshot.all_versions
3073+
3074+
context_diff = ContextDiff(
3075+
environment="test_environment",
3076+
is_new_environment=True,
3077+
is_unfinalized_environment=False,
3078+
normalize_environment_name=True,
3079+
create_from="prod",
3080+
create_from_env_exists=True,
3081+
added=set(),
3082+
removed_snapshots={},
3083+
snapshots={new_snapshot.snapshot_id: new_snapshot},
3084+
new_snapshots={new_snapshot.snapshot_id: new_snapshot},
3085+
modified_snapshots={old_snapshot.name: (new_snapshot, old_snapshot)},
3086+
previous_plan_id=None,
3087+
previously_promoted_snapshot_ids=set(),
3088+
previous_finalized_snapshots=None,
3089+
previous_gateway_managed_virtual_layer=False,
3090+
gateway_managed_virtual_layer=False,
3091+
environment_statements=[],
3092+
)
3093+
3094+
plan = PlanBuilder(
3095+
context_diff,
3096+
default_start="2025-01-02",
3097+
end="2025-01-03",
3098+
backfill_models={new_snapshot.name},
3099+
is_dev=True,
3100+
).build()
3101+
3102+
assert plan.provided_start is None
3103+
assert plan.missing_intervals == [
3104+
SnapshotIntervals(
3105+
snapshot_id=new_snapshot.snapshot_id,
3106+
intervals=[
3107+
(to_timestamp("2025-01-01"), to_timestamp("2025-01-02")),
3108+
(to_timestamp("2025-01-02"), to_timestamp("2025-01-03")),
3109+
(to_timestamp("2025-01-03"), to_timestamp("2025-01-04")),
3110+
],
3111+
)
3112+
]
3113+
3114+
29893115
@time_machine.travel("2026-06-02 00:00:00 UTC")
29903116
def test_forward_only_preview_uses_preview_start(make_snapshot):
29913117
context_diff, new_snapshot = _make_forward_only_preview_context_diff(make_snapshot)

0 commit comments

Comments
 (0)