Cross-Region Replication Drift
partialSummary
When multi-region replication falls behind during write spikes, reads directed to secondary regions silently return stale data beyond the application SLA. Unlike an outage, the system appears healthy: queries succeed, latency is normal: but users in secondary regions observe data that may be minutes or hours old, with no automatic alerting unless explicit replication lag SLO monitoring is configured.
Description
Multi-region architectures commonly route writes to a primary region and serve reads from regional secondaries for latency reduction. Under normal load, replication lag stays below 100–500ms, which is acceptable for most use cases. During write spikes: bulk imports, viral traffic events, end-of-period batch processing : the primary generates WAL or binlog at a rate that exceeds the secondary region's ability to apply it. Lag accumulates from 500ms to 5 seconds, then 30 seconds, then minutes. Reads from the secondary return increasingly stale data.
The silent nature of this failure is its defining characteristic. The database reports replication as "running" because the secondary is connected and receiving data: it simply hasn't applied it yet. Application-level health checks query the secondary and get successful responses. The staleness is invisible unless the application explicitly checks the secondary's replication_delay metric or the data has a timestamp field that reveals its age. Users notice before monitoring does: they write data, reload the page routed to a secondary, and see the old value.
Network jitter between regions amplifies the problem. Cross-region links have higher latency (40–150ms typical US-EU) and bandwidth limits compared to intra-region links. The apply thread on the secondary is single-threaded in many configurations (MySQL binlog replication is single-threaded by default without parallel replication enabled). A burst of 50,000 writes in 10 seconds at the primary may take 30–90 seconds to fully apply at the secondary, depending on network bandwidth and apply thread capacity.
The failure compounds when the secondary is used for reads that feed into write decisions. An inventory check reads stale stock levels from the secondary and returns "in stock"; the order is placed and written to the primary; the true stock level (visible only at the primary) was already zero. This produces inventory oversell: a correctness violation that manifests as a business error rather than a technical alert.
Characteristics
Triggers
- ·Write throughput spike exceeding replication apply thread capacity (>5,000 writes/second sustained for >30 seconds)
- ·Cross-region network congestion causing apply thread to stall
- ·Large transaction (bulk import, schema change) on primary generating a single large WAL event
- ·Parallel write workload on primary with single-threaded apply on secondary
- ·Schema migration executed on primary before secondary has applied preceding transactions
Detection Signals
Mitigation Strategies
Monitor secondary replication lag in real time (PostgreSQL: pg_stat_replication.write_lag; MySQL: Seconds_Behind_Master). Configure a PagerDuty alert at >5 seconds lag. Implement a lag-aware read router in the application that automatically falls back to primary reads when secondary lag exceeds a configurable threshold (e.g., 500ms for financial data, 5 seconds for content data). The router checks lag once per second and caches the result to avoid per-request overhead.
Enable multi-threaded replication on the secondary to match primary write concurrency. MySQL: set slave_parallel_workers=8 and slave_parallel_type=LOGICAL_CLOCK. PostgreSQL logical replication: use multiple subscription workers or pglogical with parallel apply. This reduces the apply bottleneck for workloads with many concurrent independent transactions, though it does not help for single large-transaction lag.
For data that must be strongly consistent across regions (financial balances, inventory counts), configure synchronous commit on the primary. PostgreSQL: synchronous_commit = remote_write for at least one secondary. MySQL: semi-sync replication with rpl_semi_sync_master_wait_for_slave_count=1. Accepts higher write latency (+40–150ms per write for cross-region ACK) in exchange for read-your-writes consistency guarantee.
Recovery Steps
- 1.Check current replication lag on all secondaries via monitoring dashboard or SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())) AS lag_seconds on PostgreSQL
- 2.If lag exceeds SLO, redirect all reads for affected region to the primary immediately
- 3.Monitor lag metric to confirm it is decreasing; lag should recover at the apply thread throughput rate
- 4.Do not restart the secondary replication process unless lag has stopped decreasing for >10 minutes: restart causes a full resync in some configurations
- 5.After lag drops below 500ms, restore read routing to the secondary
- 6.Conduct post-incident review to identify the write spike that triggered drift and evaluate parallel apply configuration
Estimated recovery time: 10–60 minutes for lag to drain after write spike subsides, depending on accumulated backlog size. Immediate mitigation (redirect reads to primary) is achievable in under 2 minutes if lag-aware routing is in place.
Affected Systems
Patterns
Technologies
Basis
Well-documented cross-region replication behavior with specific lag accumulation mechanics grounded in MySQL and PostgreSQL replication internals; silent failure characteristic is a known operational hazard in multi-region architectures