Deadlock Detection & Prevention
The Circular Wait Problem š
Deadlock occurs when two or more transactions wait for each other indefinitely, creating a circular dependency that prevents any of them from proceeding.
Classic Deadlock Example
T1: LOCK_X(Account_A)
T2: LOCK_X(Account_B)
T1: LOCK_X(Account_B) ā Waits for T2 to release B
T2: LOCK_X(Account_A) ā Waits for T1 to release A
Result: Both transactions are stuck forever! š
Deadlock Detection: Wait-for Graphs š
The Graph Approach
A wait-for graph is a directed graph where:
-
Nodes represent transactions
-
Edges represent waiting relationships (Ti ā Tj means Ti waits for Tj)
Building a Wait-for Graph
Scenario:
- T1 holds LOCK_X(A), wants LOCK_X(B)
- T2 holds LOCK_X(B), wants LOCK_X(C)
- T3 holds LOCK_X(C), wants LOCK_X(A)
Wait-for Graph:
T1 ā T2 ā T3 ā T1
Cycle detected! This indicates a deadlock.
Deadlock Detection Algorithm
def detect_deadlock(wait_for_graph):
"""
Use DFS to detect cycles IN wait-for graph
Returns: List of transactions IN deadlock (if any)
"""
def dfs(node, visited, rec_stack, path):
visited.add(node)
rec_stack.add(node)
path.append(node)
for neighbor IN wait_for_graph[node]:
if neighbor IN rec_stack:
# Found cycle - extract deadlock
cycle_start = path.index(neighbor)
return path[cycle_start:]
if neighbor NOT IN visited:
result = dfs(neighbor, visited, rec_stack, path)
if result:
return result
rec_stack.remove(node)
path.pop()
return None
for TRANSACTION IN wait_for_graph:
if TRANSACTION NOT IN visited:
cycle = dfs(TRANSACTION, SET(), SET(), [])
if cycle:
return cycle
return None # No deadlock
Deadlock Prevention Strategies š”ļø
1. Lock Ordering (Conservative Approach)
šÆ Global Lock Ordering
Rule: All transactions must acquire locks in the same predefined order
Example: Alphabetical Ordering
ā Can Deadlock
T1: LOCK(Account_B) ā LOCK(Account_A)
T2: LOCK(Account_A) ā LOCK(Account_B)
ā Deadlock-Free
T1: LOCK(Account_A) ā LOCK(Account_B)
T2: LOCK(Account_A) ā LOCK(Account_B)
T2 waits for T1 to finish, no circular wait!
ā Advantages
- Completely prevents deadlocks
- Simple to understand and implement
- No deadlock detection overhead
ā Disadvantages
- Reduces concurrency significantly
- Requires knowing all locks in advance
- May force unnecessary lock ordering
2. Timeout-Based Prevention
ā° Wait Timeout
Rule: If a transaction waits for a lock longer than a threshold, abort it
```sql -- PostgreSQL example BEGIN; SET lock_timeout = '5s'; -- Wait max 5 seconds for locks SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- If can't get lock in 5s, transaction aborted ```āļø Trade-offs
- Short timeout: Prevents long waits, but may abort valid transactions
- Long timeout: Reduces unnecessary aborts, but deadlocks persist longer
- Adaptive: Adjust timeout based on system load and transaction history
3. Timestamp-Based Protocols
Deadlock Resolution: Victim Selection šÆ
When a deadlock is detected, one transaction must be aborted to break the cycle.
Victim Selection Criteria
š Transaction Cost
- Choose transaction with least work done
- Minimize wasted computation
- Consider CPU time, I/O operations, locks held
š Transaction Age
- Abort youngest transaction (most recently started)
- Older transactions likely closer to completion
- Prevents starvation of long-running transactions
š Abort Count
- Avoid repeatedly aborting same transaction
- Track previous abort history
- Give priority to previously aborted transactions
š Lock Count
- Abort transaction holding fewest locks
- Releasing fewer locks reduces system disruption
- Other transactions can proceed faster
Victim Selection Algorithm
def select_victim(deadlock_cycle):
"""
SELECT best victim FROM deadlock cycle
Returns: TRANSACTION to abort
"""
best_victim = None
min_cost = float('inf')
for TRANSACTION IN deadlock_cycle:
# Calculate composite cost score
cost = (
TRANSACTION.work_done * 0.4 + # 40% weight
TRANSACTION.locks_held * 0.3 + # 30% weight
TRANSACTION.abort_count * (-0.2) + # 20% weight (negative = priority)
TRANSACTION.age * (-0.1) # 10% weight (negative = priority)
)
if cost < min_cost:
min_cost = cost
best_victim = TRANSACTION
return best_victim
Real-World Deadlock Handling š
PostgreSQL Approach
-- Configure deadlock detection
SET deadlock_timeout = '1s'; -- Check for deadlocks after 1 second
-- PostgreSQL automatically:
-- 1. Detects deadlocks using wait-for graphs
-- 2. Selects victim (usually newest transaction)
-- 3. Aborts victim with ERROR: deadlock detected
MySQL InnoDB Strategy
-- InnoDB deadlock detection is automatic
-- Configuration options:
SET GLOBAL innodb_deadlock_detect = ON; -- Enable detection
SET GLOBAL innodb_lock_wait_timeout = 50; -- Max wait time
-- InnoDB automatically:
-- 1. Detects deadlocks immediately
-- 2. Selects victim with least cost (estimated by undo work)
-- 3. Returns ERROR 1213: Deadlock found when trying to get lock
Performance Implications ā”
š Detection Overhead
- Wait-for graphs: O(n²) space, O(n²) time per check
- Check frequency: Every few seconds (configurable)
- Trade-off: Frequent checks vs. longer deadlock duration
š Abort Cost
- Rollback work: Undo all changes made by victim
- Lock release: Free all locks held by victim
- Retry cost: Re-execute aborted transaction
š Prevention Cost
- Lock ordering: Reduced concurrency, complex planning
- Timeouts: False positives, unnecessary aborts
- Timestamps: More frequent aborts, retry overhead
Best Practices šÆ
šÆ Application Design
- Short transactions: Reduce lock holding time
- Consistent access patterns: Use same table ordering
- Avoid user interaction: Don't wait for user input while holding locks
āļø Database Configuration
- Tune timeouts: Balance detection speed vs. false positives
- Monitor deadlocks: Track frequency and patterns
- Isolation levels: Lower levels may reduce deadlock chance
š Error Handling
- Retry logic: Automatically retry deadlock victims
- Exponential backoff: Avoid retry storms
- User feedback: Gracefully handle deadlock errors
Key Takeaways š§
šÆ Essential Points
- Deadlocks are inevitable: With 2PL, deadlocks can occur despite correctness
- Detection vs. Prevention: Trade-off between runtime overhead and concurrency
- Victim selection matters: Good algorithms minimize total system cost
- Application awareness: Smart design patterns can reduce deadlock frequency
- Modern systems handle it: Database engines automatically detect and resolve deadlocks
What's Next? š
Now you understand how to handle deadlocks. Continue learning:
-
Lock Examples - See deadlocks and resolution in action
-
Two-Phase Locking - Review how 2PL creates deadlock potential
-
Lock Foundations - Back to the basics