require-concurrent-refresh-matview
REFRESH MATERIALIZED VIEW without CONCURRENTLY blocks all reads.
- operation
- Views
- lock taken
- ACCESS EXCLUSIVE
- remediation
- Fixed by --fix
- category
- Lock safety
- Blocks reads
What triggers it
Fires on a RefreshMatViewStmt where concurrent is not set and skipData (WITH NO DATA) is not set — a plain REFRESH that actually recomputes data.
What does not
REFRESH MATERIALIZED VIEW CONCURRENTLY returns null immediately since it is already safe, and so does REFRESH MATERIALIZED VIEW ... WITH NO DATA, which just marks the view unscannable rather than locking for a real refresh.
Where it applies
Applies to every PostgreSQL version MigrationPilot targets. It works on the SQL text alone — no database connection needed.
The lock, and what it blocks
A plain REFRESH holds ACCESS EXCLUSIVE on the materialized view for the whole query that recomputes it, blocking every SELECT against the view until it commits. CONCURRENTLY avoids this by building into a new copy and swapping.
Why it matters
Without CONCURRENTLY, REFRESH acquires ACCESS EXCLUSIVE, blocking all queries against the materialized view for the entire refresh duration. With CONCURRENTLY, reads continue against the old data until refresh completes.
Unsafe, and safe
Flagged
REFRESH MATERIALIZED VIEW mv_user_stats;
Safe alternative
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_stats;What it assumes
REFRESH CONCURRENTLY requires a UNIQUE index on the materialized view; a matview without one cannot switch to CONCURRENTLY, even though the rule does not check for that prerequisite before suggesting the fix.
What the CLI prints
⚠ [MP033] WARNING (line 1) REFRESH MATERIALIZED VIEW "mv_user_stats" without CONCURRENTLY takes ACCESS EXCLUSIVE lock, blocking all reads for the entire refresh duration. Safe alternative: -- Use CONCURRENTLY to allow reads during refresh (requires a UNIQUE index): REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_stats; Why: REFRESH MATERIALIZED VIEW without CONCURRENTLY takes an ACCESS EXCLUSIVE lock for the entire refresh duration, blocking all queries against the view. REFRESH CONCURRENTLY allows reads to continue using the old data while the new data is being computed. Requires a UNIQUE index on the materialized view. Docs: https://migrationpilot.dev/rules/mp033
Generated by running the CLI's own formatter over the flagged example above, so it is the text the tool actually produces. A real run also reports the other rules that fire on the same statement; those blocks are left out here.
Turning it off
For one statement, put a comment on the line before it:
-- migrationpilot-disable MP033
REFRESH MATERIALIZED VIEW mv_user_stats;For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP033: false
# or keep it, and downgrade it
rules:
MP033:
severity: warningTry it
Open this rule's flagged example in the playground. It runs in your browser — edit it and watch the finding appear and disappear.
Run MP033 in the playground