Deadlock detection and prevention in PostgreSQL - Time & Space Complexity
When databases handle many tasks at once, sometimes they get stuck waiting for each other. This is called a deadlock.
We want to understand how the system finds and stops these deadlocks as more tasks run.
Analyze the time complexity of PostgreSQL's deadlock detection query.
WITH RECURSIVE waiting_graph AS (
SELECT
blocked.pid AS blocked_pid,
blocking.pid AS blocking_pid
FROM pg_locks blocked
JOIN pg_locks blocking ON blocked.locktype = blocking.locktype
AND blocked.database IS NOT DISTINCT FROM blocking.database
AND blocked.relation IS NOT DISTINCT FROM blocking.relation
AND blocked.page IS NOT DISTINCT FROM blocking.page
AND blocked.tuple IS NOT DISTINCT FROM blocking.tuple
AND blocked.virtualxid IS NOT DISTINCT FROM blocking.virtualxid
AND blocked.transactionid IS NOT DISTINCT FROM blocking.transactionid
AND blocked.classid IS NOT DISTINCT FROM blocking.classid
AND blocked.objid IS NOT DISTINCT FROM blocking.objid
AND blocked.objsubid IS NOT DISTINCT FROM blocking.objsubid
WHERE blocked.granted = false AND blocking.granted = true
),
search_cycle AS (
SELECT blocked_pid, blocking_pid, ARRAY[blocked_pid] AS path
FROM waiting_graph
UNION ALL
SELECT wg.blocked_pid, wg.blocking_pid, path || wg.blocked_pid
FROM waiting_graph wg
JOIN search_cycle sc ON wg.blocked_pid = sc.blocking_pid
WHERE NOT wg.blocked_pid = ANY(path)
)
SELECT * FROM search_cycle WHERE blocking_pid = ANY(path[1:array_length(path,1)-1]);
This query looks for cycles in the waiting graph to detect deadlocks among transactions.
Look for repeated checks and traversals in the query.
- Primary operation: Recursive search through the waiting graph to find cycles.
- How many times: The recursion repeats for each edge in the graph until a cycle is found or all paths are checked.
The more transactions and locks there are, the bigger the waiting graph becomes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20-30 recursive checks |
| 100 | Hundreds of recursive checks |
| 1000 | Thousands to tens of thousands of checks |
Pattern observation: The work grows quickly as the number of waiting relationships grows, because the system must explore many paths to find cycles.
Time Complexity: O(n + e)
This means the detection time grows roughly with the number of transactions and their waiting connections.
[X] Wrong: "Deadlock detection only checks a fixed number of transactions, so it always runs fast."
[OK] Correct: The detection must explore all waiting links between transactions, which can grow a lot as more transactions wait on each other.
Understanding how deadlock detection scales helps you explain how databases keep running smoothly even with many users.
"What if the database used a simpler method that only checked direct waits, not recursive cycles? How would the time complexity change?"