In complex microservice architectures executing multi-resource transactions (e.g. reserving inventory across sharded SQL databases, acquiring Redis Redlock leases, and reserving payment holds), circular dependencies can create catastrophic distributed deadlocks. The Chandy-Misra-Haas (CMH) edge-chasing algorithm provides decentralized, asynchronous deadlock detection by propagating lightweight probe messages along the edges of the distributed Wait-For-Graph (WFG) without requiring a centralized lock coordinator.
The Architecture of Edge-Chasing Probe Propagation
How probe messages detect circular dependencies without global synchronization:
When process $P_i$ is blocked waiting for resource $R$ held by process $P_j$, $P_i$ initiates a probe $(i, j, k)$ where $i$ is the initiator, $j$ is the sender, and $k$ is the recipient. If $P_k$ is transitively blocked on $P_m$, it forwards probe $(i, k, m)$. If process $P_i$ ever receives a probe carrying its own initiator ID $(i, *, i)$, a circular dependency is mathematically proven, prompting $P_i$ to abort and release its held locks.
Deadlock Resolution Strategies Compared
| Resolution Strategy | Architecture Model | Message Overhead | False Deadlock Rate |
|---|---|---|---|
| Lock Lease TTL Timeouts | Optimistic timeout expiration | Zero network probe overhead | High (Aborts valid slow transactions) |
| Centralized WFG Coordinator | Global lock manager service | High ($O(N)$ lock state reporting) | Moderate (State lag during failovers) |
| Chandy-Misra-Haas Edge Chasing | Decentralized Asynchronous Probing | Low (Probes sent only when blocked) | Zero Phantom Deadlocks |
Chandy-Misra-Haas Probe Handler in TypeScript
Asynchronously forwarding edge-chasing probes in distributed microservices:
export interface CMHProbe {
initiatorId: string;
senderId: string;
recipientId: string;
}
export class DistributedLockAgent {
private holdingProcessId: string;
private waitingOnProcessIds: Set<string> = new Set();
constructor(holdingProcessId: string) {
this.holdingProcessId = holdingProcessId;
}
public handleIncomingProbe(probe: CMHProbe): { isDeadlocked: boolean; forwardedProbes: CMHProbe[] } {
// Check if probe returned to initiator
if (probe.initiatorId === this.holdingProcessId) {
return { isDeadlocked: true, forwardedProbes: [] };
}
// Forward probe along all outgoing Wait-For-Graph edges
const forwardedProbes: CMHProbe[] = [];
for (const targetProcessId of this.waitingOnProcessIds) {
forwardedProbes.push({
initiatorId: probe.initiatorId,
senderId: this.holdingProcessId,
recipientId: targetProcessId
});
}
return { isDeadlocked: false, forwardedProbes };
}
}
Explore Advanced Distributed Engineering & Architecture
Build resilient distributed systems with provable concurrency guarantees. Read our guide on Deterministic Time-Travel Debugging & Record-and-Replay, explore cross-currency swap modeling on FinanceQuickly XCCY Collateral, review telematics spoliation on CarInjuryAttorney HOS Forensics, or consult with our distributed systems architects.