Question presented to candidate: "Two of our payment workers sometimes fail with a 'deadlock detected' error. What is a deadlock, how does the database find and break one, and what would you change in the code so it stops happening?"
What a strong answer should cover:
- Definition: two or more transactions each hold a lock the other needs, so every one of them waits forever; a cycle in the waits-for graph
- The difference from ordinary blocking: a lock wait ends when the holder commits, a deadlock never ends on its own
- Detection: PostgreSQL runs its deadlock check after
deadlock_timeout(default 1s) and aborts one transaction with SQLSTATE40P01; InnoDB detects immediately by default (error 1213); SQL Server picks a victim with error 1205 - Prevention: lock rows in one consistent order (sort the keys,
ORDER BY id FOR UPDATE), keep transactions short, take the strongest lock first, touch fewer rows (good indexes) - Handling: deadlocks can never be ruled out completely, so the application retries the whole transaction on
40P01;lock_timeoutbounds ordinary waits - Diagnosis: the error DETAIL, the server log,
pg_locksandpg_blocking_pids()
Clarifying questions expected:
- Which database engine, and do we have the full error text or server log for one occurrence?
- Do the two workers touch the same rows in a different order (for example a transfer A to B and a transfer B to A)?
Code / implementation expected: Yes โ reproduce the deadlock with two sessions, then fix it by locking rows in a consistent order.
Schema used by the examples: