Synchronous vs Asynchronous
Async replication favors throughput and low commit latency; sync replication narrows RPO at the cost of standby latency and availability sensitivity.
Recipe
Default async on primary; add one sync standby for tier-0 data.
-- Async (default): commits return when local WAL is flushed
ALTER SYSTEM SET synchronous_commit = 'on';
-- Sync to one named standby
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby1)';
SELECT pg_reload_conf();-- On standby: set application_name to match synchronous_standby_names
ALTER SYSTEM SET cluster_name = 'standby1';
SELECT pg_reload_conf();When to reach for this: You need zero or near-zero committed transaction loss on primary failure and can accept added commit latency.
Working Example
Measure sync impact and verify partner state:
-- Primary: inspect sync partners
SELECT application_name, sync_state, sync_priority, write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
-- Session-level durability trade-off (bulk load window)
SET synchronous_commit = 'off';
COPY staging.events FROM '/data/events.csv' CSV;
RESET synchronous_commit;# Benchmark commit latency async vs sync (pgbench)
pgbench -h primary.db.internal -U app -c 4 -j 4 -T 30 -S appdb
pgbench -h primary.db.internal -U app -c 4 -j 4 -T 30 -S appdb \
--sync-method=strictWhat this demonstrates:
synchronous_standby_namespicks which standbys count toward quorum.sync_state = 'sync'confirms the partner participates in synchronous commits.synchronous_commit = offspeeds bulk loads but widens RPO for that session only.
Deep Dive
How It Works
- Async: Primary returns
COMMITafter local WAL flush (persynchronous_commit). Standby catches up later. - Sync: Primary waits until required standbys flush (and optionally apply) WAL before
COMMITreturns. synchronous_commitlevels:off,local,remote_write,remote_apply,on(maps tosynchronous_standby_namesquorum).FIRST n (...)means any n listed standbys satisfy quorum;ANY n (...)is an alternate quorum style in PostgreSQL 18.
RPO and RTO Snapshot
| Mode | Typical RPO on primary crash | Commit latency | Failover notes |
|---|---|---|---|
| Async | Last committed seconds of WAL | Lowest | Promote standby; may lose recent commits |
| Sync (1 partner) | Zero committed loss if partner healthy | + RTT to standby | Failover if partner dead blocks commits unless you relax sync |
remote_apply | Zero loss; reads on standby see commit | Highest | Strongest consistency, slowest commits |
Patroni 3.x Note
Patroni sets synchronous_mode and manages synchronous_standby_names during failover. Test Patroni sync config in staging before production; a lost sync standby can stall writes.
Gotchas
- Two sync standbys without latency budget - Commit waits on slowest geo replica. Fix: At most one sync partner unless you model WAN RTT.
- Sync partner down - Writes hang if quorum cannot be met. Fix: Patroni failover policy or temporarily reduce
synchronous_standby_names. synchronous_commit = offglobally - Fast but RPO widens for all sessions. Fix: Scope to maintenance windows or batch roles only.- Misnamed application_name - Standby never becomes sync partner. Fix: Match names in
pg_stat_replication.application_name. - Confusing sync with logical replication - Different protocol and guarantees. Fix: Physical sync is WAL quorum; logical is change stream.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Async + frequent base backups | Cross-region DR with loose RPO | Financial ledger needs zero loss |
| Logical replication | Version upgrade, selective tables | You need synchronous physical quorum |
synchronous_commit = remote_write | Balance latency vs durability | You require standby replay visibility before commit |
FAQs
How many sync standbys should I run?
One local sync partner is the common production pattern. More partners increase latency and failure modes.
Does sync replication block reads on the primary?
No. Only committing transactions wait for standby acknowledgment.
What happens if the sync standby dies?
Commits block until quorum is restored or you change synchronous_standby_names. Patroni can demote sync mode automatically.
Is remote_apply always better?
It waits for standby replay, not just flush. Use only when applications read from standby immediately after commit.
Can PgBouncer affect sync behavior?
PgBouncer pools connections but does not change PostgreSQL sync semantics. Session settings like synchronous_commit apply per backend connection.
Related
- Streaming Replication Basics - setup
- Replication Slots - WAL retention
- Patroni & etcd/Consul - sync mode automation
- DR Basics - RPO/RTO definitions
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.