Database Replication
debt(d7/e5/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints show automated detection is 'no' — replication lag issues and stale reads typically surface only during production load or careful testing. Tools like datadog and prometheus can monitor lag after setup, but detecting the architectural misconfiguration (all reads hitting primary, no lag handling) requires manual review or runtime observation of production behavior.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix indicates configuring read/write splitting in Laravel/Symfony config plus adding monitoring infrastructure. While the config change itself may be localized, properly routing time-sensitive reads to primary requires auditing application code paths to identify which reads occur post-write, touching multiple files across the codebase.
Closest to 'strong gravitational pull' (b7). Database replication is a cross-cutting architectural choice that affects every database interaction — applies_to shows it spans web and cli contexts. Once in place, every query must consider whether it should hit primary or replica, and the replication strategy shapes how the entire application handles data consistency. This is a persistent tax on development decisions.
Closest to 'serious trap' (t7). The misconception explicitly states developers assume read replicas have the same data as primary, but async replicas have lag. This contradicts the mental model from simpler database setups where reads always return fresh data. Reading from replica immediately after write returning stale data is a non-obvious failure mode that catches developers who expect database reads to be consistent.
Also Known As
TL;DR
Explanation
Synchronous replication: primary waits for at least one replica to confirm before acknowledging the write — zero data loss guarantee, but higher write latency. Asynchronous replication: primary acknowledges immediately, replica catches up — lower latency, but a primary failure can lose committed transactions. Logical replication copies row-level changes (compatible with different schema versions). Physical/streaming replication copies WAL binary — exact byte-for-byte replica. Use cases: read replicas (scale reads), failover (high availability), geographic distribution, and analytics offload.
Diagram
flowchart LR
subgraph Sync Replication
P1[Primary] -->|wait for ack| R1[Replica]
R1 -->|confirmed| P1
NOTE1[Zero data loss<br/>Higher write latency]
end
subgraph Async Replication
P2[Primary] -->|fire and forget| R2[Replica]
P2 -->|ack immediately| APP2[App]
NOTE2[Low latency<br/>May lose recent writes on failure]
end
style NOTE1 fill:#238636,color:#fff
style NOTE2 fill:#d29922,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Reading from replica after write without accounting for replication lag.
- Synchronous replication for all replicas — one slow replica blocks all writes.
- No monitoring of replication lag — lag can grow silently to hours under heavy write load.
- Failing over without promoting the most up-to-date replica — partial data loss on failover.
Code Examples
// Read after write from replica — stale data:
$this->primaryDb->update('users', ['name' => 'Alice'], ['id' => 42]);
// Replication lag: 200ms
$user = $this->replicaDb->find('users', 42); // Returns old name for 200ms
return $user['name']; // 'Bob' — stale! User just changed it to 'Alice'
// Read-your-writes: use primary for fresh data after writes:
public function updateAndReturn(int $id, array $data): array {
$this->primaryDb->update('users', $data, ['id' => $id]);
// Read from primary — guaranteed fresh:
return $this->primaryDb->find('users', $id);
}
// Other reads from replica — fine with eventual consistency:
public function getPublicProfile(int $id): array {
return $this->replicaDb->find('users', $id); // Stale OK for public data
}