Eventual Consistency in Databases
debt(d7/e7/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). The term's detection_hints explicitly state 'automated: no' — there are no automated tools that can reliably detect eventual consistency misuse. Issues manifest as subtle data inconsistencies that only appear under load, during network partitions, or in production when users report seeing stale/conflicting data. Code review might catch obvious anti-patterns like immediate read-after-write, but the systemic issues require runtime observation.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix mentions WebSocket/SSE but that addresses only the UI symptom, not the root consistency model choice. Fixing eventual consistency misuse—like switching from an AP database to a strongly consistent one for financial data, or implementing read-your-writes routing—requires changes across data access layers, potentially database migration, and rearchitecting how the application handles reads and writes. This isn't a single-file fix; it's a fundamental data layer change.
Closest to 'strong gravitational pull' (b7). The choice of consistency model is architectural—it applies to web/api contexts and shapes how every feature that touches that data store must be designed. Once you've built features assuming eventual consistency (or incorrectly assuming strong consistency from an eventually consistent store), every new feature must account for this. The common_mistakes show this burden: routing decisions, data model choices for financial vs. non-financial data, and handling replication lag all flow from this one architectural decision.
Closest to 'serious trap' (t7). The misconception field directly states developers believe 'eventual consistency means data is always correct after a few milliseconds' when in reality 'under continuous write load, divergence can persist indefinitely.' This contradicts intuition from working with traditional single-node databases where writes are immediately visible. The common_mistakes reinforce this: developers assume sub-100ms lag, read immediately after write expecting consistency, and conflate different types of consistency issues.
Also Known As
TL;DR
Explanation
Eventual consistency (AP systems in CAP) allows reads from different nodes to return different values temporarily. DynamoDB, Cassandra, CouchDB are AP by default. Techniques: read-your-writes (user always reads from the node they wrote to), monotonic reads (user never sees older data than previously seen), causal consistency (operations that causally depend see each other in order), and vector clocks for conflict detection. PHP applications must handle stale reads explicitly — cache invalidation bugs are a form of eventual consistency issue.
Diagram
sequenceDiagram
participant APP as PHP App
participant PRI as Primary DB
participant REP as Replica
APP->>PRI: UPDATE cart - add item
PRI-->>APP: OK - committed
Note over PRI,REP: Replication lag: 200ms
APP->>REP: SELECT * FROM cart
REP-->>APP: Old cart - item missing!
Note over APP,REP: Read-your-writes fix:<br/>route to primary after write
Common Misconception
Why It Matters
Common Mistakes
- Reading from an eventual consistent store immediately after writing without read-your-writes routing.
- Using eventual consistency for financial data — bank balances require strong consistency.
- Assuming eventual consistency lag is always under 100ms — under network partitions it can be unbounded.
- Conflating Redis replication lag with application-level eventual consistency bugs.
Code Examples
// Eventual consistency bug — reads stale data:
function addToCart(int $userId, int $productId): void {
$this->cartDb->write($userId, $productId); // Written to primary
}
function getCart(int $userId): array {
return $this->cartDb->read($userId); // Read from replica — may not have the write yet!
// User adds item, sees empty cart — support ticket: 'cart lost my items'
}
// Read-your-writes: route to primary for consistency:
function addToCart(int $userId, int $productId): void {
$this->cartDb->write($userId, $productId);
// Invalidate cache — force next read from primary:
$this->cache->delete("cart:{$userId}");
}
function getCart(int $userId): array {
// Session-based routing: if user wrote recently, use primary:
$db = $this->userWroteRecently($userId) ? $this->primary : $this->replica;
return $db->read($userId);
}