Repository navigation
fix(redshift): do not recreate existing views on every evaluation - #6126
Draft
gandeevanraghuramandd wants to merge 1 commit into
Draft
gandeevanraghuramandd wants to merge 1 commit into
gandeevanraghuramandd wants to merge 1 commit into
Conversation
On engines without view binding, ViewStrategy.insert recreated an existing view on every interval evaluation. On Redshift this is a DROP + CREATE, which gives the view a new OID and causes concurrent queries reading through it to fail with "could not open relation with OID". Add an engine adapter attribute RECREATE_VIEW_ON_EVALUATION (default True) and set it to False for Redshift, so an existing regular view is only recreated on the first insert of a snapshot version, which also covers rebuilds forced by should_force_rebuild. Materialized views and all other engines are unchanged. Signed-off-by: Gandeevan Raghuraman <gandeevan.raghuraman@doordash.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Problem
VIEW models have intervals like any other model kind (default cron
@daily), so every plan or run that fills a missing interval for a view callsViewStrategy.insert. For engines without view binding (HAS_VIEW_BINDING = False),must_recreate_viewis alwaysTruefor regular views, so an existing view is recreated on every interval evaluation even when its definition has not changed.On Redshift this is particularly disruptive:
RedshiftEngineAdapterinheritsBasePostgresEngineAdapter.create_view, which withreplace=TrueissuesDROP VIEW IF EXISTS ... CASCADEfollowed byCREATE VIEW ... WITH NO SCHEMA BINDING.could not open relation with OID <n>.DROPtakes an exclusive lock and can queue behind long-running readers.sqlmesh__analytics.analytics__example_view__<version>) recreates it underneath queries running in another environment, which makes these failures more likely.Change
RECREATE_VIEW_ON_EVALUATION(defaultTrue, so current behavior is preserved), next to the existingRECREATE_MATERIALIZED_VIEW_ON_EVALUATION.RECREATE_VIEW_ON_EVALUATION = FalseonRedshiftEngineAdapter.ViewStrategy.insert, when the adapter opts out, an existing regular (non-materialized) view is only recreated whenis_first_insertisTrue. Otherwise it takes the existing "Skipping creation of the view" path. This mirrors howRECREATE_MATERIALIZED_VIEW_ON_EVALUATIONalready gates materialized views.is_first_insertis the right signal because it is already computed as(not intervals or not target_table_exists) and batch_index == 0, so it covers both cases where a view really needs recreating:should_force_rebuild(new.is_view and new.is_indirect_non_breaking and not new.is_forward_only) cleared the view's intervals so it gets repointed after an upstream change.Routine interval fills of an existing view have
is_first_insert=Falseand no longer issue DDL on Redshift.Unchanged:
ViewStrategy.create, which already never replaces an existing view.Test Plan
Added
test_evaluate_existing_view_recreationintests/core/test_snapshot_evaluator.py. It runsSnapshotEvaluator.evaluateagainst realRedshiftEngineAdapter/SnowflakeEngineAdapterinstances with mocked connections and an existing physical view, then checks the view DDL that gets issued:is_first_insert)False)DROP/CREATE VIEWTrue)DROP VIEW+CREATE VIEWCREATE OR REPLACE VIEW(unchanged)CREATE OR REPLACE VIEW(unchanged)CREATE OR REPLACE MATERIALIZED VIEW(unchanged)With the source change reverted, only the Redshift routine-evaluation case fails, as expected.
Commands run locally (Python 3.12):
pytest tests/core/test_snapshot_evaluator.py -k test_evaluate_existing_view_recreation: 6 passedpytest -n 8 tests/core/test_snapshot_evaluator.py tests/core/engine_adapter/test_redshift.py tests/core/engine_adapter/test_starrocks.py tests/core/integration/test_forward_only.py: 350 passed (one earlier run had a single failure intest_forward_only.py::test_full_history_restatement_model_regular_plan_preview_enabledthat did not reproduce in isolation, serially, or in 3 further parallel runs)pytest -n auto -m "fast and not cicdonly and not isolated" tests/core: 2082 passed, 3 skippedmake style: ruff, ruff-format, mypy, valid migrations all passedNot tested against a live Redshift cluster.
Open questions for maintainers
is_first_insertis set. I have not verified this against a live cluster. The case I am least sure about is a forward-only additive upstream change behindSELECT *.should_force_rebuildskips forward-only snapshots, and SQLMesh rendersSELECT *into an explicit column list (the generated DDL looks likeCREATE VIEW ... ("a") AS SELECT "a" AS "a" ...), so with this change the existing view would probably not expose the new column until it is recreated for some other reason. Previously the next interval evaluation would have recreated it with the new column list. Is that acceptable for Redshift, or should this case still trigger a recreation (for example by comparing the rendered column list with the existing view's columns)?materialized trueVIEW models.RedshiftEngineAdapterhasSUPPORTS_MATERIALIZED_VIEWS = False, so a VIEW model withmaterialized trueis created as a regular view on Redshift. It is still recreated on every evaluation through the materialized-view path, which this PR deliberately leaves unchanged. Should it get the same treatment?Checklist
make styleand fixed any issuesmake fast-test): ran the fast-marked tests undertests/coreplus the files listed above, not the fullmake fast-testtargetgit commit -s) per the DCOThis change and description were drafted by an AI coding agent (Cursor, Claude) on behalf of the author and reviewed before opening.
Made with Cursor