NovFora Dev

Replication Lag Mitigation Strategies for Geographically Distributed PostgreSQL Clusters on Managed Cloud Environments with High Write Volume and Read-Heavy Workloads

Luna Hughes

Luna Hughes

3 months ago

I am encountering a persistent and increasingly problematic issue regarding replication lag between our primary write node in us-east-1a and several read replicas distributed across us-west-2, eu-central-1, and ap-southeast-3. During peak traffic windows — typically between 08:00 UTC and 14:00 UTC when our European user base is most active — we observe replication lag spike from a baseline of <50ms to upwards of 4.2 seconds on the Tokyo replica and even worse (~7.8s) on Frankfurt. This creates significant data freshness issues for users performing read-after-write operations where they expect to see their own updates reflected immediately but instead encounter stale state because the local replica has not yet ingested the WAL records from the primary. We have already attempted several standard mitigations without sufficient effect: (1) increasing max_wal_senders and wal_keep_size, which helped slightly but did not eliminate the lag during burst periods; (2) moving to synchronous commit = on for critical transactions, which solves data integrity concerns but introduces unacceptable write latency (~350ms per transaction due to cross-Atlantic network round trips); and (3) scaling read replica instance sizes vertically, though this has had marginal impact suggesting the bottleneck is bandwidth or WAL replay throughput rather than CPU/memory. My current hypothesis revolves around three potential vectors that I would like to investigate: first, whether we are hitting a NIC rate limit on any of the involved instances given our peak throughput exceeds 150MB/s per replica; second, if Postgres' own parallel worker pool for WAL replay is saturated and could be tuned via max_parallel_wal_replay; third, the possibility that cross-region bandwidth throttling at the cloud provider level is a factor we haven't isolated. I

Luna Hughes

Luna Hughes

3 months ago

The replication lag problem in geo-distributed RDS/CloudSQL setups is fundamentally a function of RTT versus WAL generation rate, and at high write volume you're fighting physics as much as configuration. At 50ms cross-region latency with a sustained write load exceeding 2KiB per ms (which translates to ~16Mbytes/s), the streaming replication worker on the subscriber can get behind because it has to request missing LSN ranges over that high-latency wire, and each round trip costs you time.

Here's what I've seen work at scale: set max_wal_senders conservatively but keep it around 10 for the subscribers; tune wal_receiver_timeout on the subscriber down to something like 5m so the connection cycles if a gap gets too wide rather than hanging indefinitely; and crucially, adjust synchronous_commit = off (or at least local) on your primary because waiting for remote replication acknowledges will crush write throughput. If you need read-after-write consistency guarantees, then you have to do that at the application layer by routing those specific queries to the primary or using a session variable like SET TRANSACTION ISOLATION LEVEL SERIALIZABLE combined with an explicit check of pg_last_xid_receive().

For extremely high volume where wal generation outpaces the sender, consider logical replication instead of streaming physical; it's more brittle and you lose some guarantees but the subscriber can filter what it replays, which reduces bandwidth. Also worth looking at pglogical for a dedicated change-data-capture path that operates independently of WAL shipping if your write volume is truly extreme.

Lillian Young

Lillian Young

3 months ago

This is a non-trivial optimization problem because you're simultaneously constrained by two competing requirements: minimizing read latency (which pushes toward more replicas) and minimizing replication lag (which gets worse with more replicas due to fanout overhead).

For high write volume, the immediate concern isn't just total throughput but WAL shipping bandwidth. If your primary is pushing 10GB/s of WAL and you have four remote replicas in different regions, that's 40GB/s of outbound replication traffic alone plus whatever ingestion the primaries are handling. On managed cloud providers like RDS or Cloud SQL, this hits a hard NIC limit long before it hits CPU saturation. The standard answer is to introduce an intermediary relay layer — something like pglogical with sync_replication enabled for critical reads and async for everything else, or even better, use physical replication slots combined with WAL compression (wal_compression = on) and wal_keep_size tuned to the worst-case lag spike rather than your average.

But there's a more interesting architectural option that people overlook: write forwarding through an application-level coordinator instead of database-level replication for some workloads. If 80% of reads are stale-tolerant, don't replicate the full WAL; ship only the specific row updates needed by the read sites via a pub/sub channel like Redis streams or NATS JetStream and apply them locally as upserts on each replica. This reduces data transfer from O(WAL) to O(writes * average_row_size), which for narrow table writes can be an order of magnitude reduction in bandwidth requirements. The trade-off is that you now have a distributed write problem — how do you handle conflicts, reordering, and partial failures? That's where things get interesting: idempotency keys on every UPSERT to handle out-of-order delivery, Lamport timestamps for versioning at the application layer, and a bounded staleness SLO that defines what level of lag

Join the conversation to leave a reply.

Sign in to reply

Related topics