Skip to content

adapter: revalidate OR REPLACE drop set in staged create finish#37652

Draft
ggevay wants to merge 1 commit into
MaterializeInc:mainfrom
ggevay:gabor/sql-521-sink-drop-dependency-race
Draft

adapter: revalidate OR REPLACE drop set in staged create finish#37652
ggevay wants to merge 1 commit into
MaterializeInc:mainfrom
ggevay:gabor/sql-521-sink-drop-dependency-race

Conversation

@ggevay

@ggevay ggevay commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Fixes SQL-521

Important

Statistical verification with the Parallel Workload cancel scenario (the
reproducer from SQL-521, on PR #37590's branch) was deliberately deferred
and remains TODO before merging. Verification so far is the deterministic
failpoint-based repro and the new regression workflow, validated in both
directions (fails before the fix, passes after).

Motivation

CREATE OR REPLACE [MATERIALIZED] VIEW computes the set of items to drop
(the item being replaced) at planning time, but sequencing is staged: the
optimize stage runs off the coordinator thread and the drop set is only
committed later, in the finish stage. Most DDL cannot interleave because it
queues on the coordinator's DDL lock, but statements with off-thread
purification (e.g. CREATE SINK, see must_serialize_ddl) are exempt from
that lock and only check that their own dependencies still exist. A sink
created on the materialized view being replaced could therefore commit inside
the window. The finish stage then dropped the old item anyway, leaving the
sink with uses/references edges to a missing item. The catalog consistency
check panics the coordinator on that very transact:

thread 'coordinator' panicked at src/adapter/src/coord/ddl.rs:122:9:
assertion `left == right` failed: coordinator inconsistency detected
  ... MissingUses { object_a: User(819), object_b: User(798) } x2 ...

(The MissingUses entry appears twice because the sink's references() and
uses() both contain the dropped item id.)

The same window also allowed the item being replaced to disappear entirely,
via a concurrent ALTER MATERIALIZED VIEW ... APPLY REPLACEMENT (likewise
exempt from the DDL lock), in which case the finish stage would panic while
generating the drop ops.

Description

User-facing change: when a new dependent (e.g. a CREATE SINK) commits
concurrently with a CREATE OR REPLACE MATERIALIZED VIEW / CREATE OR REPLACE VIEW, the CREATE OR REPLACE statement now fails with a clean,
retryable error instead of corrupting the catalog and panicking the
coordinator. If the item being replaced was concurrently dropped, the
statement now fails with a "was dropped" error instead of panicking.

  • Add Coordinator::validate_or_replace_drop_ids, called at the top of
    create_materialized_view_finish and create_view_finish (in the same
    message handler that commits the ops, so no further DDL can interleave). It
    recomputes the replaced item's dependent closure against the current
    catalog and compares it with the plan-time drop set:
    • set changed: AdapterError::ChangedPlan("the set of objects that depend on <kind> <name> changed while the statement was executing")
    • replace target gone: AdapterError::ConcurrentDependencyDrop
  • Add a create_materialized_view_optimize failpoint in the off-thread
    optimize stage.
  • Add a deterministic regression test: test/race-condition workflow
    create-or-replace-race, which parks a CREATE OR REPLACE MATERIALIZED VIEW at the failpoint, commits a CREATE SINK on the replaced MV inside
    the window, and asserts the replacement aborts cleanly with the sink and
    the old definition intact. Wired into Nightly as the
    race-condition-create-or-replace step.

Verification

  • New create-or-replace-race workflow passes with the fix and correctly
    fails (detecting the coordinator panic) with the finish-side check
    disabled.
  • A timing-based repro on unmodified main (CREATE SINK purification
    committing during a slow off-thread optimize of the OR REPLACE) reproduced
    the exact SQL-521 panic signature, and produces the new clean error with
    the fix.
  • test/sqllogictest/materialized_views.slt,
    replacement-materialized-views.slt, rbac_views.slt pass (normal
    OR REPLACE and replacement paths unchanged).
  • TODO before merge: Parallel Workload soak (--scenario=cancel --complexity=ddl --threads=8 on the PR parallel-workload: broaden SQL-surface and action coverage #37590 branch, which adds the
    CreateOrReplaceView action) to statistically confirm the panic is gone.
    Note that PR parallel-workload: broaden SQL-surface and action coverage #37590 will then need "changed while the statement was
    executing" and "was dropped" in CreateOrReplaceViewAction. errors_to_ignore.

🤖 Generated with Claude Code

CREATE OR REPLACE [MATERIALIZED] VIEW computes the set of items to drop
(the item being replaced) at planning time, but sequencing is staged and
commits that set only in the finish stage, after off-thread
optimization. Most DDL cannot interleave because it queues on the
coordinator DDL lock, but statements with off-thread purification
(e.g. CREATE SINK) are exempt and only check that their dependencies
still exist. A sink created on the replaced materialized view could
therefore commit inside the window. The finish stage then dropped the
old item anyway, leaving the sink with dependency edges to a missing
item and panicking the coordinator catalog consistency check (SQL-521).

The finish stages now recompute the replaced item dependent closure
against the current catalog and abort with a retryable error if it
changed since planning. This also covers the item being replaced
disappearing entirely, e.g. through a concurrent ALTER MATERIALIZED
VIEW APPLY REPLACEMENT, which is likewise exempt from the DDL lock.

Add a create_materialized_view_optimize failpoint and a deterministic
regression test in test/race-condition, wired into the Nightly
pipeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ggevay ggevay added the A-ADAPTER Topics related to the ADAPTER layer label Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ADAPTER Topics related to the ADAPTER layer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant