Skip to content

Commit 8e8ad74

Browse files
fix(state_sync): skip interval removal when there is nothing to remove
`remove_intervals` unconditionally called `insert_append`, which raises "Cannot construct source query from an empty DataFrame" when there are no intervals to remove. With `remove_shared_versions=True` the rows to write are rebuilt by querying the `_intervals` table for rows sharing the target name/versions. When none of the affected snapshots have any rows there, for example an un-backfilled snapshot version held by another environment, the resulting set is empty and the insert fails. This surfaced as a non-zero exit code at the end of an otherwise successful prod restatement, after the model batches had already been backfilled and committed. Return early instead, matching the guards already present on the neighbouring insert paths `add_snapshots_intervals` and `_push_snapshot_intervals`. Fixes #5909 Signed-off-by: Amir Vakili <AVakili@Voleon.com>
1 parent 869248c commit 8e8ad74

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

‎sqlmesh/core/state_sync/db/interval.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ def remove_intervals(
108108
for snapshot in all_snapshots
109109
]
110110

111+
if not intervals_to_remove:
112+
return
113+
111114
if logger.isEnabledFor(logging.INFO):
112115
snapshot_ids = ", ".join(str(s.snapshot_id) for s, _ in intervals_to_remove)
113116
logger.info("Removing interval for snapshots: %s", snapshot_ids)

‎tests/core/state_sync/test_state_sync.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,40 @@ def test_remove_interval_missing_snapshot(
400400
]
401401

402402

403+
def test_remove_interval_no_matching_intervals(
404+
state_sync: EngineAdapterStateSync, make_snapshot: t.Callable
405+
) -> None:
406+
snapshot = make_snapshot(
407+
SqlModel(
408+
name="a",
409+
cron="@daily",
410+
query=parse_one("select 1, ds"),
411+
),
412+
version="a",
413+
)
414+
state_sync.push_snapshots([snapshot])
415+
416+
# The snapshot has never been backfilled, so there are no rows to expand the shared versions from
417+
state_sync.remove_intervals(
418+
[(snapshot, snapshot.inclusive_exclusive("2020-01-15", "2020-01-17"))],
419+
remove_shared_versions=True,
420+
)
421+
422+
remove_records_count = state_sync.engine_adapter.fetchone(
423+
"SELECT COUNT(*) FROM sqlmesh._intervals WHERE name = '\"a\"' AND version = 'a' AND is_removed"
424+
)[0] # type: ignore
425+
assert remove_records_count == 0
426+
427+
assert not state_sync.get_snapshots([snapshot])[snapshot.snapshot_id].intervals
428+
429+
430+
def test_remove_interval_empty_input(state_sync: EngineAdapterStateSync) -> None:
431+
state_sync.remove_intervals([])
432+
state_sync.remove_intervals([], remove_shared_versions=True)
433+
434+
assert state_sync.engine_adapter.fetchone("SELECT COUNT(*) FROM sqlmesh._intervals")[0] == 0 # type: ignore
435+
436+
403437
def test_refresh_snapshot_intervals(
404438
state_sync: EngineAdapterStateSync, make_snapshot: t.Callable
405439
) -> None:

0 commit comments

Comments
 (0)