Engineer synchronous commit guarantees deliberately by separating remote write, flush, and apply acknowledgments from availability and application-visible latency.
Synchronous Replication, Quorum Commit, Latency, and Failure Semantics
Engineer synchronous commit guarantees deliberately by separating remote write, flush, and apply acknowledgments from availability and application-visible latency.
Learning outcomes
Asynchronous streaming minimizes commit latency, but a primary failure can lose transactions that the application was told had committed if those WAL records had not reached a usable standby. Synchronous replication changes the commit contract by making selected commits wait for standby acknowledgments. The important question is: which acknowledgment?
Explain FIRST priority and ANY quorum standby selection.
Map synchronous_commit modes to local commit, remote write, remote flush, and remote apply evidence.
Observe sync_state and commit waiting rather than assuming a configured name is active.
Inject a controlled standby outage and explain availability behavior.
Choose quorum/latency policy from failure tolerance instead of treating synchronous replication as “zero data loss” magic.
Synchronous replication strengthens acknowledgement semantics. It does not automatically perform failover, fence the old primary, route clients, guarantee that every named standby is healthy, or make two sites independent of common storage/network failures.
1. Name the standby explicitly
synchronous_standby_names matches a standby's
application_name. If names are duplicated,
selection can be indeterminate. Set a deliberate application
name in the standby's primary_conninfo and verify
what the primary sees.
primary_conninfo = 'host=localhost port=55436 user=ch14_repl application_name=servicehub_sync1'primary_slot_name = 'ch14_standby_slot'
SELECT application_name, state, sync_state, sync_priority, write_lsn, flush_lsn, replay_lsnFROM pg_stat_replication;
A standby must reach streaming before it can act as
a synchronous standby. Configuration text alone is not proof of
protection.
2. FIRST means priority; ANY means quorum
synchronous_standby_names = 'FIRST 1 (servicehub_sync1, servicehub_sync2)'# Wait for one selected standby; earlier names have higher priority.synchronous_standby_names = 'FIRST 2 (servicehub_sync1, servicehub_sync2, servicehub_sync3)'# Wait for the two highest-priority available synchronous standbys.
synchronous_standby_names = 'ANY 2 (servicehub_sync1, servicehub_sync2, servicehub_sync3)'# Wait for replies from any two candidates.
FIRST is useful when topology has preferred nodes.
ANY expresses a quorum: any required number of
listed candidates may satisfy the commit. Neither syntax tells
PostgreSQL to build or repair the nodes.
3. synchronous_commit chooses the acknowledgment depth
The standby can acknowledge at different stages.
remote_write waits until the synchronous standby
has written the WAL to its operating system, but not necessarily
flushed it to stable storage. on waits for remote
flush. remote_apply waits until replay has applied
the transaction, making it visible to queries on the synchronous
standby in simple cases.
| Mode | Commit waits for | What it does not prove |
|---|---|---|
local |
local durable flush, not synchronous standby | remote receipt |
remote_write |
remote OS write acknowledgement | remote durable flush / visibility |
on |
remote durable flush | replay / query visibility |
remote_apply |
remote replay/application | automatic failover or client routing |
off |
asynchronous local acknowledgement behavior | local immediate flush or remote receipt |
BEGIN;SET LOCAL synchronous_commit = 'remote_write';UPDATE app.ch14_work_orders SET status='dispatched' WHERE work_order_id=14001;COMMIT;BEGIN;SET LOCAL synchronous_commit = 'on';UPDATE app.ch14_work_orders SET status='onsite' WHERE work_order_id=14001;COMMIT;BEGIN;SET LOCAL synchronous_commit = 'remote_apply';UPDATE app.ch14_work_orders SET status='completed' WHERE work_order_id=14001;COMMIT;
Measure latency locally with the same workload and topology. The minimum additional latency depends on network round trips and standby processing; there is no universal millisecond value.
4. Connect the commit mode to observed LSNs
SELECT application_name, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lagFROM pg_stat_replication;
The lag columns estimate how long recent WAL took to reach write, flush, and replay acknowledgement. PostgreSQL documents them as measurements relevant to the corresponding synchronous-commit levels, not catch-up ETA values.
5. Failure injection: commits can wait
Configure the disposable primary to require the only standby,
confirm it is sync, then stop the standby. A write
transaction using synchronous_commit=on can finish
its local work and wait for required standby confirmation. From
another primary session, inspect the wait rather than killing
random processes.
SELECT pid, usename, state, wait_event_type, wait_event, xact_start, queryFROM pg_stat_activityWHERE wait_event = 'SyncRep' OR wait_event = 'WaitForStandbyConfirmation';
Exact wait-event naming can differ by wait point and version, so
interpret both the SQL session state and
pg_stat_replication. Restart the standby and
observe the blocked commit complete. This is the availability
cost of requiring a synchronous acknowledgment.
6. Do not “fix” an outage by silently weakening guarantees
If required synchronous standbys are unavailable, operators
sometimes remove them from
synchronous_standby_names to restore write
availability. That is a business continuity decision that
weakens the durability contract. Make it explicit, logged,
authorized, and reversible. PostgreSQL cannot decide your
acceptable data-loss window.
SELECT name, setting, context, source, pending_restartFROM pg_settingsWHERE name IN ('synchronous_standby_names','synchronous_commit');
“Synchronous replication means zero data loss” is too broad. The guarantee depends on the chosen synchronous_commit level, which standbys are actually selected/streaming, common-mode failures, whether a failover candidate contains the acknowledged WAL, and whether failover is correctly fenced and routed.
7. Quorum design requires a failure model
ANY 2 (a,b,c) tolerates loss of one candidate while
still satisfying a two-standby quorum, assuming the surviving
two are reachable and sufficiently current.
FIRST 1 chooses one preferred synchronous node with
potential replacements. Across regions, increased round-trip
latency may dominate application response time. Keep quorum
membership aligned with failure domains; three VMs on one
storage system are not three independent durability domains.
8. Verify the synchronous set during topology change
A standby listed in synchronous_standby_names is
not automatically synchronous. It must be connected, identified
by a matching application_name, and in a state
eligible for synchronous selection. During startup or catch-up,
it may remain asynchronous/potential. Operational automation
should wait for observed state='streaming' and the
expected sync_state before declaring the configured
durability policy restored.
SELECT application_name, state, sync_state, sync_priority, flush_lsn, replay_lsn, reply_timeFROM pg_stat_replicationORDER BY application_name;
For ANY quorum, candidates show
sync_state='quorum'; for priority-based selection,
active synchronous members show sync and later
candidates may show potential. Build alerts around
the required count and state, not only the GUC string.
9. Acknowledged does not mean promoted
Suppose a transaction returns success under
synchronous_commit=on. PostgreSQL has received the
required remote durable flush acknowledgments before returning
success. If the primary then fails, the HA controller must still
promote a candidate that actually has that WAL and fence any
competing writer. Synchronous commit narrows the durability
uncertainty; orchestration determines whether the protected copy
becomes the authoritative service.
Likewise, a client timeout is ambiguous: the server might have committed locally and even synchronously replicated the transaction before the client stopped waiting. Applications need idempotency keys or transaction-result reconciliation rather than assuming “timeout means rollback.” This connects Chapter 07's retry design directly to HA behavior.
CREATE TABLE IF NOT EXISTS app.ch14_commands ( request_key text PRIMARY KEY, work_order_id bigint NOT NULL, requested_state text NOT NULL, created_at timestamptz NOT NULL DEFAULT clock_timestamp());INSERT INTO app.ch14_commands(request_key, work_order_id, requested_state)VALUES ('dispatch-14001-v1', 14001, 'dispatched')ON CONFLICT (request_key) DO NOTHINGRETURNING *;
10. Per-transaction durability can be a feature
synchronous_commit can be set per transaction. A
system can reserve remote_apply for operations that
require immediate replica visibility while using
on for most durable writes and asynchronous
behavior for explicitly expendable telemetry. The prerequisite
is a documented business classification; ad hoc developer
overrides turn the durability model into guesswork.
11. Application visibility and causal reads
If an application commits with remote_apply to a
selected synchronous standby, that standby has replayed the
commit before the primary acknowledges it, which can support
read-after-write routing to that standby in simple topologies.
With on, the WAL may be durable remotely but not
yet visible to standby queries. This is a consistency contract,
not simply a performance knob.
Check your understanding
- What is the difference between FIRST and ANY?
- What does synchronous_commit=on wait for remotely?
- Which mode waits for standby replay?
- Why can write availability decrease under synchronous replication?
- Why is one synchronous VM in the same failure domain not equivalent to an independent durable copy?
Review the answers
FIRST chooses synchronous nodes by priority; ANY waits for a quorum from the candidate set. on waits for remote durable flush. remote_apply waits for replay. Required acknowledgments can block commits when standbys are unavailable. Shared power/storage/network can defeat the assumed independence of copies.
Authoritative references
Replication and HA behavior is topology-, version-, privilege-, and operating-system-sensitive. These primary PostgreSQL sources define the mechanisms used in this lesson.