A transaction reads a customer balance, performs another query and then reads the balance again. Meanwhile, another session commits an update. Whether the second read sees that update depends on the isolation level, not merely on the presence of a transaction.
PostgreSQL’s isolation documentation explains its Repeatable Read implementation: ordinary queries use a stable snapshot established at the transaction’s first non-control statement. Later commits from other transactions do not enter that snapshot. The transaction still sees its own earlier changes.
Rehearse the difference with two test sessions
- Use a disposable test database containing one simple row.
- In session A, begin a Repeatable Read transaction and read the row.
- In session B, change that row and commit.
- Read again in session A, then end its transaction and read in a new one.
The useful observation is the boundary between the old snapshot and the new transaction. Record the actual results and database version. Do not run an improvised concurrency exercise against production data.
A stable snapshot is not the same as a guarantee that every business rule remains safe under concurrent writes. PostgreSQL distinguishes Repeatable Read from Serializable, and some combinations of transactions can still produce serialization anomalies at the former level.
Updates can also encounter conflicts that require a transaction to be retried. Application code must handle the database’s error and retry the complete relevant transaction according to its design, rather than repeating an arbitrary final statement.
Keep transactions purposeful and avoid leaving a test session open while you inspect unrelated work. Before choosing an isolation level, name the consistency requirement: a stable report view, protection of a business invariant, or something else. Then test the actual failure case with the database and version you use. Isolation labels from another system are not a substitute for its documented behaviour.

