Overview
Optimistic locking assumes conflicts are rare. Read the row with a version (or updated_at), do the work, then UPDATE ... WHERE id = $1 AND version = $2; zero rows updated means another writer got there first. Apply it to edit forms and APIs where two users rarely change the same row but a silent overwrite would lose work.
Rules
- On a conflict, surface it to the caller (HTTP
412 Precondition FailedwithETagandIf-Match, or409) or re-read and retry. Do not retry blindly. - No locks are held, so there is no deadlock risk and contention stays low.
- Use pessimistic
SELECT ... FOR UPDATEwhen conflicts are frequent or retries are expensive. It blocks other writers. - ORM support: JPA and Hibernate have
@Version. Prisma has no version attribute; add an integer column and filter on it in thewhereofupdate(non-unique filters are allowed since Prisma 5).
SELECT id, balance, version FROM accounts WHERE id = 42;
UPDATE accounts SET balance = balance - 100, version = version + 1
WHERE id = 42 AND version = 7; -- 0 rows updated = conflictRelated
- deadlock: the risk optimistic locking avoids.
- transaction-isolation: what the initial SELECT sees.
- acid: the guarantees the update runs under.
- postgres:
FOR UPDATEand row locks. - prisma-transactions: version-checked updates in Prisma.