Eventual Consistency
debt(d9/e5/b7/t5)
Closest to 'silent in production until users hit it' (d9). The detection_hints explicitly state 'automated: no' and describe the pattern as a read-after-write expecting immediate consistency. There is no static analysis tool that can catch this architectural assumption mismatch — it only surfaces when users observe stale data in production, often intermittently depending on replication lag.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests designing reads to tolerate stale data and routing certain reads to the primary, but the common_mistakes reveal multiple dimensions: UI changes to communicate lag to users, operational changes (monitoring replication lag), identifying which operations require strong consistency and rerouting them. This is not a single-line patch — it touches read paths, UI/UX, and operational monitoring across the codebase.
Closest to 'strong gravitational pull' (b7). Eventual consistency applies to web, api, and queue-worker contexts and is an architectural-level choice. Once a system is built on eventually-consistent stores or async consumers, every feature that reads after writing must account for it. The applies_to scope (web, api, queue-worker) means virtually every developer touching the system is shaped by this constraint — cache invalidation, user-facing feedback, financial safety checks all bend around it.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field captures a real but correctable misunderstanding: developers believe inconsistency may be permanent or unbounded rather than milliseconds-to-seconds. The common_mistakes reinforce this — reading immediately after a write expecting the new value is a classic, widely-documented gotcha that most distributed systems developers encounter and learn from, but it is not catastrophic in the way that contradicts a nearly universal mental model.
Also Known As
TL;DR
Explanation
Eventual consistency (AP in CAP) allows read replicas and distributed caches to serve slightly stale data until replication catches up. Two concurrent readers may briefly see different values after a write. Practical PHP implications: never assume a read immediately following a write returns the updated value when using read replicas; implement compensating logic (re-fetch after write, optimistic UI updates with eventual server sync). Many business domains naturally tolerate eventual consistency (social feeds, shopping cart totals) while others require strong consistency (financial account balances, inventory reservation).
Diagram
flowchart TD
WRITE[Write to primary] --> PRI[(Primary DB)]
PRI -->|async replicate| R1[(Replica 1<br/>lag 100ms)]
PRI -->|async replicate| R2[(Replica 2<br/>lag 300ms)]
READ[Read from replica] --> R1
R1 -->|may return| STALE[Stale data<br/>100ms behind]
subgraph Mitigations
RYW[Read-your-writes<br/>route to primary after write]
SESSION[Session consistency<br/>same replica per user]
MONO[Monotonic reads<br/>never go backwards]
end
style STALE fill:#f85149,color:#fff
style RYW fill:#238636,color:#fff
style SESSION fill:#238636,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Building UIs that read immediately after a write and expecting the new value — the read may hit an out-of-date replica.
- Not communicating eventual consistency to users — showing a spinner or 'update in progress' manages expectations.
- Using eventual consistency for operations that require strong consistency (financial transfers, inventory deduction).
- Not monitoring replication lag — eventual consistency without lag metrics is a blind spot.
Code Examples
// Reads own write assuming strong consistency:
$db->insert('orders', $orderData);
$order = $db->readFromReplica('SELECT * FROM orders WHERE id = ?', [$id]);
// May return null — replica may not have caught up yet
// Fix: read-after-write from primary, or use read-your-writes session consistency
// Eventual consistency — system converges to consistent state over time
// Common in: read replicas, distributed caches, event-driven systems
// Handle stale reads gracefully:
public function getOrderStatus(int $orderId): string {
// Read from replica (may be 100ms behind primary)
$order = Order::on('replica')->find($orderId);
// Show last-known status with staleness indicator if needed:
return [
'status' => $order?->status ?? 'processing',
'as_of' => $order?->updated_at,
'note' => 'Status may take a moment to update',
];
}
// After write, redirect to a page that doesn't require fresh data:
// POST /orders → 202 Accepted → poll /orders/{id}/status
// Or: write to primary, read from primary immediately after write (read-your-writes):
$order = Order::on('primary')->find($orderId);