← PostgreSQL: operations and recovery
08 / 12 · 70 MIN

Timeouts, savepoints and deadlocks

Choose recovery scope from the error, transaction state and effects that may still be committed.

Bound the right wait

While A retains the lock, B uses an 80-millisecond lock_timeout and a five-second statement_timeout. Its UPDATE attempt fails with 55P03 and a lock-timeout diagnostic. The limit concerns acquiring a lock; it is not a total budget for the whole transaction. The script configures short values only in exercise sessions to make behavior observable. It does not recommend these values for production. In an actual service, associate limits with the latency budget, retry feasibility and client behavior when an attempt ends without performing the update.

One SQLSTATE can have different operational causes

Next, B uses an 80-millisecond statement_timeout and a one-second lock_timeout. The statement ends first and returns 57014 with a statement-timeout message. Earlier explicit cancellation also produced 57014, but from a different origin. Retain code, diagnostic and effective configuration before assigning cause. An application should use structured codes to classify errors without losing details needed for investigation. The workshop neither measures timer precision nor promises that an operation ends at an exact instant. It shows which limit interrupted the query under controlled conditions and confirms the need to recover the transaction.

Use a savepoint with explicit intent

B begins a transaction, inserts note before and creates retry_step. The blocked UPDATE fails; B rolls back to the savepoint, inserts after and commits. Both notes are recorded, but the blocked update did not occur. This is a scope choice, not automatic replay of the entire transaction. The example shows that work preceding the savepoint remains eligible for commit. In an actual workflow, ask whether committing those effects without the main update respects the requirement. If the business operation is indivisible, retaining notes alone is not correct recovery and may require full rollback.

Reproduce a wait cycle

Two fresh sessions update different rows, subtracting ten units. Each then tries to update the row retained by the other. The observer confirms the first wait before starting the second, forming the cycle under controlled ordering. PostgreSQL interrupts one transaction with 40P01 and allows the other to continue. The test requires exactly one victim without choosing which PID must lose. Detection does not mean every participant was rolled back. After cleaning up the rejected transaction and committing the survivor, both rows hold 90: the victim’s partial work was undone and both survivor updates were committed.

Change ordering without promising universal deadlock freedom

The last experiment has both sessions request rows in the same order. The first retains row 1; the second waits there. The first can update row 2 and commit, allowing the second to update both. Both commits leave 80 in each row. Consistent ordering eliminates the specific reproduced cycle but does not establish that an entire application is deadlock-free. Triggers, additional relations and other code paths can introduce dependencies. Retain error handling and a bounded strategy for retrying the complete transaction with fresh reads and reconciliation of external effects when present.

Deliver a verifiable recovery decision

For the fictional workshop, present the error and ask the participant to identify connection state, committed work and the next authorized command. An answer saying only retry is insufficient: it should distinguish full rollback, return to a savepoint, a new transaction and intervention on the blocker. The workshop records actual results and removes its cluster after joining every query thread. It implements neither application retries, pools nor external-request deduplication. Representative validation should include those elements, attempt limits, observability and functional criteria, alongside specialist human review before claiming operational readiness.

-- Run only in the disposable workshop with its owned fixture.
BEGIN;
INSERT INTO notes VALUES (1, 'before');
SAVEPOINT retry_step;
SET LOCAL lock_timeout = '80ms'
-- If the following statement fails, the client must choose recovery scope:
UPDATE balances SET units = 70 WHERE id = 1;
-- The teaching branch uses ROLLBACK TO SAVEPOINT retry_step;
-- then inserts its second note and commits only that intended partial scope.
IN PRACTICE

lock_timeout produces 55P03; a shorter statement_timeout produces 57014. A savepoint permits step recovery without losing earlier notes.

Common pitfalls

Classifying every wait as deadlock; repeating only the final UPDATE after abort; assuming a fixed victim; converting timeout into permission to commit.

Related topics: Transactions and concurrency · Diagnosis and support handover

Take this idea with you

Correct recovery depends on failed scope and the business requirement. An error code alone does not determine what to retry.

Create account

Reference: Statement, lock and transaction timeouts · PostgreSQL 18 reference semantics;18.6 current stable at review

PostgreSQL® is a registered trademark of PostgreSQL Community Association. bigsavant.com is an independent preparation platform and is not affiliated with, associated with, sponsored, authorised or endorsed by PostgreSQL Community Association. Content and questions are original, are not official exam questions, and completing our tests does not award or guarantee any certification. Names are used only to identify the subject. All other trademarks belong to their respective owners.