Starvation & Livelock
debt(d9/e5/b5/t7)
Closest to 'silent in production until users hit it' (d9). The detection_hints field explicitly states 'automated: no', and the code_pattern (while.*retry|sleep.*retry) only hints at the structure, not the pathological timing interaction. Starvation and livelock manifest only under specific load conditions or timing coincidences in production; no standard linter or SAST tool catches the logical failure.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix calls for adding exponential backoff with jitter, setting maximum retry counts, and switching to fair queues — changes that typically span multiple services, queue configurations, and retry-loop call sites rather than a single-line patch. It's not a full architectural rework but clearly more than a parameterised one-liner.
Closest to 'persistent productivity tax' (b5). The applies_to covers web, cli, and queue-worker contexts, meaning retry and scheduling logic across all three contexts must be designed with jitter and fairness in mind. Every new retry loop or queue consumer added to the codebase inherits this concern, imposing an ongoing tax without necessarily reshaping the whole architecture.
Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception field states developers believe livelock is rare, when in fact it emerges commonly in distributed retry logic when two services conflict at fixed intervals — a scenario that is extremely common in microservice architectures. Fixed retry intervals look obviously correct (simpler code, predictable timing) but are precisely the cause of the problem, making the 'obvious' implementation the wrong one.
TL;DR
Explanation
Starvation: a low-priority thread waits indefinitely while high-priority threads keep acquiring the resource. Solutions: fair scheduling, priority ageing (priority increases with wait time), FIFO queues for lock acquisition. Livelock: threads are not blocked but actively change state in response to each other — making no net progress. Classic: two processes each detect conflict and back off simultaneously, then retry simultaneously, indefinitely. Solutions: randomised back-off (exponential backoff with jitter), coordinator, timeout with fallback. Livelock is harder to detect than deadlock — CPU is busy but work is not progressing.
Common Misconception
Why It Matters
Common Mistakes
- Fixed retry interval — causes convoy/thundering herd that can create livelock.
- No priority ageing — low-priority jobs wait forever under load.
- Not adding jitter to retry backoff.
Code Examples
// Fixed interval retry — livelock risk:
while (!acquireLock()) {
sleep(1); // Both workers retry at same time indefinitely
}
// Exponential backoff with jitter:
$attempt = 0;
while (!acquireLock()) {
$delay = min(30, (2 ** $attempt)) + rand(0, 1000) / 1000; // Jitter
sleep($delay);
if (++$attempt > 10) throw new LockTimeoutException();
}