Race Condition
debt(d9/e5/b5/t7)
Closest to 'silent in production until users hit it' (d9). Semgrep patterns can catch some file read-modify-write patterns, but most race conditions are timing-dependent and surface only under load. Intermittent and hard to reproduce.
Closest to 'touches multiple files / significant refactor in one component' (e5). Quick_fix suggests swapping to flock(), atomic UPDATE, SELECT FOR UPDATE, or Redis INCR — each fix is localized but identifying every read-modify-write site and adding proper locking typically spans multiple call sites in a component.
Closest to 'persistent productivity tax' (b5). Applies across web/cli/queue contexts; once concurrency is in play, every shared-state operation must be reasoned about for atomicity, slowing many work streams.
Closest to 'serious trap' (t7). The misconception (PHP is single-threaded therefore race-free) directly contradicts the PHP-FPM reality of concurrent processes sharing files/DB/cache. Developers coming from a single-request mental model guess wrong systematically.
TL;DR
Explanation
Race conditions arise when multiple threads/processes access shared mutable state without synchronisation. Classic example: two threads both read a counter (value: 5), both increment locally, both write (both write 6 instead of 7). The TOCTOU (Time-Of-Check-To-Time-Of-Use) variant: check a condition then act on it, but state changes between check and act. In PHP: race conditions occur in file-based counters (read→increment→write without locks), cache stampede (multiple processes regenerating expired cache simultaneously), and database reads without transactions. Prevention: atomic operations, mutexes/locks, database transactions, optimistic locking.
Common Misconception
Why It Matters
Common Mistakes
- File-based counters without flock() — classic race condition.
- Cache aside pattern without stampede protection.
- Check-then-act on database records without SELECT FOR UPDATE.
- Assuming single-threaded PHP is race-condition-free.
Code Examples
// Race condition — read/increment/write without lock:
$count = (int)file_get_contents('counter.txt');
$count++;
file_put_contents('counter.txt', $count);
// Two simultaneous requests both read 5, both write 6
// File lock prevents race:
$fp = fopen('counter.txt', 'c+');
if (flock($fp, LOCK_EX)) {
$count = (int)stream_get_contents($fp);
$count++;
ftruncate($fp, 0); rewind($fp);
fwrite($fp, $count);
flock($fp, LOCK_UN);
}
fclose($fp);
// Better: use atomic DB increment:
$pdo->exec('UPDATE counters SET value = value + 1 WHERE id = 1');