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 Failed with ETag and If-Match, or 409) 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 UPDATE when 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 the where of update (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 = conflict