Snapshot isolation is a guarantee about what a transaction can see. When a transaction begins under snapshot isolation, PostgreSQL records the current transaction ID (XID) as the snapshot boundary. Any row version committed after that XID is invisible to the transaction, regardless of when the transaction reads the row.
The practical effect is that readers and writers do not block each other. A long-running report query does not hold locks that prevent concurrent INSERTs or UPDATEs. Each transaction sees a stable, consistent view of the database for its entire duration.
What Snapshot Isolation Means
Snapshot isolation is implemented on top of MVCC (Multi-Version Concurrency Control). PostgreSQL stores multiple versions of each row, each tagged with the XID of the transaction that created it. When a transaction reads under snapshot isolation, it only sees row versions whose creating XID is less than or equal to its own snapshot XID and that are committed.
This means:
- Dirty reads are impossible. Uncommitted row versions from other transactions are never visible.
- Non-repeatable reads are prevented. The same query returns the same rows each time within the transaction, because the snapshot does not advance.
- Write-write conflicts are detected. If two transactions try to update the same row, one will be aborted with a serialization error.
Writers use the same MVCC machinery. An UPDATE creates a new row version rather than overwriting the existing one, leaving the old version visible to snapshot readers until VACUUM reclaims it.
Where Snapshot Isolation Appears in PostgreSQL
PostgreSQL exposes snapshot isolation through two isolation levels:
REPEATABLE READ uses a transaction-level snapshot. All reads in the transaction see the same committed data from the moment the first query ran. Non-repeatable reads are prevented. Phantom reads from INSERT are also prevented because the snapshot is fixed. Write-skew anomalies involving concurrent non-overlapping writes are still possible.
SERIALIZABLE extends REPEATABLE READ by adding predicate locking — PostgreSQL tracks what data each transaction read and checks whether any concurrent transaction could have logically affected the result. Serialization failures are reported as errors the application must handle by retrying the transaction.
READ COMMITTED does not use snapshot isolation at the transaction level. PostgreSQL takes a fresh snapshot at the start of each individual statement. Two queries in the same transaction can see different committed data if another transaction commits between them.
Vela branches start from a consistent snapshot. Changes in a branch never affect the source. Learn How Vela Works
Why Snapshot Isolation Matters for Production Postgres
The most common pain point snapshot isolation solves is long-running read queries blocking writes — or vice versa. Without MVCC-backed snapshot isolation, a reporting query holding a shared lock would block any concurrent UPDATE on the same table. With snapshot isolation, readers and writers work from their own versions and neither blocks the other.
For production systems this matters in several scenarios:
- Analytics on the main database. Snapshot isolation lets reporting queries run concurrently with OLTP writes without degrading write throughput.
- Migration scripts. A migration that reads a large table to backfill data can run under REPEATABLE READ to get a stable view while applications continue writing.
- Audit and compliance queries. A compliance query that must see a consistent state of the data at a point in time relies on snapshot semantics to ensure the query result is internally consistent.
The cost of snapshot isolation is MVCC overhead: row bloat from retained old versions, and VACUUM work to reclaim space. Systems with high update rates need well-tuned autovacuum to prevent table bloat.
| Isolation Level | Snapshot taken | Dirty read | Non-repeatable read | Phantom read |
|---|---|---|---|---|
| READ COMMITTED | Per statement | Prevented | Possible | Possible |
| REPEATABLE READ | Per transaction | Prevented | Prevented | Prevented (in PG) |
| SERIALIZABLE | Per transaction + predicate locks | Prevented | Prevented | Prevented |
How Snapshot Isolation Relates to Vela
Snapshot isolation is the mechanism that makes Vela database branches trustworthy as testing environments. A Vela branch starts from a consistent snapshot of the source database. The snapshot is taken at a known transaction boundary, meaning the branch reflects a coherent, fully committed state — not a partially written intermediate.
When you run tests, schema migrations, or RLS policy validation in a Vela branch, you are working with data that is consistent by the same guarantee PostgreSQL provides for REPEATABLE READ transactions. The branch does not diverge from reality because of timing — it is grounded in a specific, stable snapshot.
This also means that changes you make inside the branch — schema changes, data modifications, configuration experiments — are isolated to the branch. The source database snapshot is unaffected. The copy-on-write storage layer keeps the branch divergence contained while sharing unchanged data with the baseline.
Operational Checks
- Check the default isolation level with
SHOW default_transaction_isolation;and confirm it matches your application’s assumptions - Review MVCC bloat with
SELECT relname, n_dead_tup FROM pg_stat_user_tables ORDER BY n_dead_tup DESC - Confirm autovacuum is running and keeping up with write volume to prevent snapshot horizon buildup
- For long-running reports, consider REPEATABLE READ explicitly rather than relying on READ COMMITTED defaults
Related Vela Reading
Start with How Vela Works, Database Branching, Branch per PR, and the Vela articles library. For adjacent glossary terms, review MVCC (Multi-Version Concurrency Control), Transaction Isolation Levels, Transaction, and Copy-on-Write (COW).