8.2 Synchronous vs. Asynchronous Replication
Key Takeaways
- Asynchronous replication is PostgreSQL's default replication mode: the primary confirms transaction commit immediately after flushing WAL to its local disk without waiting for standby confirmation, providing maximum throughput and zero network latency penalty at the risk of non-zero data loss (RPO > 0) upon primary failure.
- Synchronous replication guarantees zero data loss (RPO = 0) by blocking the primary commit acknowledgement until one or more designated standbys acknowledge receipt or disk persistence of the WAL payload.
- The synchronous_standby_names parameter supports both priority-based failover lists (e.g. 'standby1, standby2') where only the highest-priority live node is active sync, and quorum-based lists (e.g. 'ANY 2 (node1, node2, node3)') where any N nodes must acknowledge.
- The 5 levels of synchronous_commit (off, local, remote_write, on, remote_apply) allow granular, per-transaction tuning between disk durability, network latency, and read-after-write causal consistency across standbys.
- Setting synchronous_commit = remote_apply eliminates standby read lag by guaranteeing that changes are replayed and queryable on synchronous standbys before the committing client receives a success response, at the cost of the highest commit latency.
8.2 Synchronous vs. Asynchronous Replication
[!IMPORTANT] The Tradeoff Frontier: In distributed database design, high availability and replication are governed by an inescapable engineering tradeoff: transaction commit latency and throughput versus data loss prevention. PostgreSQL allows administrators to position their clusters anywhere along this spectrum—from pure asynchronous replication prioritizing raw speed to multi-node synchronous quorum replication guaranteeing strict zero data loss (RPO = 0).
Understanding how PostgreSQL controls transaction durability across network boundaries requires mastering the synchronous_standby_names parameter, the internal mechanics of the synchronous_commit hierarchy, and the operational risks of replica stall.
Asynchronous Replication: High Performance with RPO Exposure
By default, PostgreSQL operates in asynchronous replication mode (synchronous_standby_names = '').
Commit Sequence Under Asynchronous Replication
- A client application executes an
INSERT,UPDATE, orDELETEstatement inside a transaction block. - The client issues
COMMIT. - The primary backend process writes the transaction's commit WAL record to WAL buffers in shared memory and flushes it to the primary's local disk via
fsync. - Immediate Client Acknowledgment: As soon as the local
fsyncreturns successfully, the primary server releases row and table locks, and sends aCOMMITsuccess message back to the client application. - Background Transmission: Concurrently and independently, the
walsenderprocess detects the new WAL records and transmits them asynchronously across the network socket to the standby replica.
Client Primary Backend Local Disk walsender Standby
| | | | |
|--- COMMIT -------------->| | | |
| |--- write & fsync ----->| | |
| |<-- disk flush ok ------| | |
|<-- COMMIT SUCCESS -------| | | |
| | | | |
| | |--- stream WAL -------->| |
| | | |==== TCP Stream ====>|
Advantages and Disadvantages
- Throughput & Latency: Client commit latency is completely decoupled from network bandwidth, network jitter, physical geographic distance, and standby performance. If the standby slows down or the network drops completely, primary transaction throughput remains unaffected.
- Recovery Point Objective (RPO > 0): Because the primary acknowledges commits before WAL is guaranteed to have left the primary node, an abrupt primary crash (e.g., immediate host motherboard failure or unrecoverable hypervisor crash) can result in data loss. Transactions that committed locally but whose WAL records were still in flight or unsent are lost if a standby is promoted to primary.
Synchronous Replication: Zero Data Loss Architecture
To achieve a Recovery Point Objective of zero (RPO = 0), PostgreSQL supports synchronous replication. Under synchronous replication, the primary backend process does not confirm a COMMIT to the calling application until the required number of standby servers have acknowledged receipt and processing of the corresponding WAL record.
Internal Sync-Rep Queue Mechanics
- The primary backend flushes the commit WAL record to local disk.
- Instead of returning to the client, the backend inserts its current LSN into an internal shared memory queue (
SyncRepQueue) and enters an efficient sleep state on a process latch. - The
walsendertransmits the WAL record to the standby. - The standby's
walreceiverprocesses the WAL payload and sends back aStandbyReplyMessagewith its updated LSN status. - Upon receiving the standby acknowledgment,
walsenderinspects theSyncRepQueue, identifies all backends whose commit LSNs have been satisfied, and awakens their latches. - The primary backends release their transactional locks and return
COMMITsuccess to their respective clients.
Standby Candidate Selection: synchronous_standby_names
The primary server determines which standbys participate in synchronous confirmation using the synchronous_standby_names parameter. Standby nodes are identified by their application_name, which is specified in each standby's primary_conninfo.
PostgreSQL supports two distinct parsing formats for synchronous_standby_names:
1. Priority-Based Selection
A comma-separated list of standby application names without keywords (or explicitly prefixed with FIRST):
# Priority list: node1 is the active synchronous standby
synchronous_standby_names = 'node1, node2, node3'
# Equivalent explicit syntax: FIRST 1
synchronous_standby_names = 'FIRST 1 (node1, node2, node3)'
- Behavior: PostgreSQL evaluates the listed nodes in strict left-to-right order. The first reachable, active standby in the list becomes the active synchronous standby (
sync_state = 'sync'inpg_stat_replication). - Potential Standbys: The remaining connected nodes are marked as potential (
sync_state = 'potential'). Ifnode1disconnects or crashes, PostgreSQL immediately elevatesnode2to become the active synchronous standby, ensuring uninterrupted zero-data-loss protection. - Multiple Priority Standbys: Specifying
FIRST 2 (node1, node2, node3)requires the first two active standbys in the list to acknowledge each commit.
2. Quorum-Based Selection (ANY)
A quorum configuration where any arbitrary subset of the listed standbys satisfies the synchronous commit requirement:
# Quorum list: Any 2 of the 3 nodes must confirm
synchronous_standby_names = 'ANY 2 (node1, node2, node3)'
- Behavior: The primary does not care which specific nodes confirm, so long as at least $N$ nodes acknowledge receipt. In
pg_stat_replication, all eligible connected standbys are marked withsync_state = 'quorum'. - Performance Benefit: Quorum replication eliminates the "tail latency" of waiting for a single specific slow node. If
node1experiences a momentary I/O spike, faster acknowledgments fromnode2andnode3satisfy the quorum, allowing client commits to proceed without delay.
The 5 Tiers of synchronous_commit
The configuration parameter synchronous_commit controls the exact operational guarantee and wait boundary required before a transaction reports success. Crucially, synchronous_commit can be configured globally in postgresql.conf, per user/database, or dynamically changed within a single transaction session using SET LOCAL synchronous_commit.
-- Tune durability for a specific bulk batch transaction
BEGIN;
SET LOCAL synchronous_commit = off;
INSERT INTO clickstream_logs SELECT ...;
COMMIT;
1. synchronous_commit = off (Local Asynchronous Commit)
- Wait Point: The backend reports
COMMITsuccess to the client application immediately after writing the WAL record into the primary's memory WAL buffers—before flushing to local disk! - Durability: If the primary crashes, the last few transactions (governed by
wal_writer_delay, default 200ms) may be lost, even though the client received a success message. Database integrity is preserved (no corruption), but transactional durability is relaxed. - Use Case: High-frequency, non-critical logging, caching, or time-series data where ingestion speed outweighs absolute durability.
2. synchronous_commit = local (Local Synchronous Only)
- Wait Point: The backend waits for the commit record to be written and flushed to the primary's local disk (
fsync). It completely ignoressynchronous_standby_namesand does not wait for any standby acknowledgment. - Durability: Standard single-instance ACID durability. Survives primary crashes, but risks data loss (RPO > 0) upon primary failover.
3. synchronous_commit = remote_write (Standby OS Write)
- Wait Point: The primary backend waits until the primary has flushed the WAL locally AND the synchronous standby's
walreceiverhas acknowledged writing the WAL to the standby's operating system cache (write_lsn). - Durability: The primary does not wait for the standby to execute an
fsyncto disk. The data is safe if the PostgreSQL server process on the standby crashes (since the OS kernel will write out cached buffers), but data could be lost if the standby server suffers a simultaneous unbuffered power outage.
4. synchronous_commit = on (Default Synchronous Mode: Standby Disk Flush)
- Wait Point: The primary backend waits until the primary has flushed locally AND the synchronous standby has written and flushed the WAL to durable disk storage (
flush_lsn). - Durability: True RPO = 0 disaster recovery durability. Even if both the primary and standby suddenly lose power simultaneously, all committed transactions are fully durable on disk.
- Latency: Every client write transaction incurs the round-trip network latency to the standby plus the standby's physical disk
fsyncwrite time.
5. synchronous_commit = remote_apply (Standby Redo / Query Visibility)
- Wait Point: The primary backend waits until the primary has flushed locally AND the standby's
startupprocess has fully replayed the WAL record into the standby's database pages (replay_lsn). - Durability & Consistency: Provides causal consistency ("read-your-writes" consistency). When a client commits a write on the primary and immediately issues a
SELECTagainst the standby replica, the newly committed data is guaranteed to be visible immediately, eliminating standby replication read lag. - Latency: Incurs the highest latency penalty, as the primary must wait for the standby's CPU and I/O subsystems to complete the physical redo logic.
| Level | Waits for Local Disk? | Standby Acknowledgment Point | Standby Durability | Standby Read Lag? | Commit Latency |
|---|---|---|---|---|---|
off | No (RAM only) | None | None | Yes (Lagging) | Lowest (Sub-ms) |
local | Yes (fsync) | None | None | Yes (Lagging) | Low |
remote_write | Yes (fsync) | Standby OS memory (write_lsn) | Survives PG crash | Yes (Lagging) | Medium (Network RTT) |
on | Yes (fsync) | Standby Disk (flush_lsn) | Survives Power Outage | Yes (Lagging) | High (RTT + Standby fsync) |
remote_apply | Yes (fsync) | Standby Pages (replay_lsn) | Survives Power Outage | Zero (Read-Your-Writes) | Highest (RTT + fsync + Redo) |
The Synchronous Availability Hazard: The Primary Stall
A critical operational hazard inherent to synchronous replication is write stall during replica failure.
[!CAUTION] The Synchronous Standby Lockup: If
synchronous_standby_namesis configured to require 1 standby, and that standby replica crashes, suffers a network partition, or is shut down for maintenance, all subsequent write transactions (INSERT,UPDATE,DELETE,COMMIT) on the primary server will block and hang indefinitely waiting for a standby acknowledgment! Read-only queries (SELECT) will continue to function normally, but the application's write pipeline will freeze completely.
To prevent unplanned production write lockups, high-availability architectures should:
- Deploy at least two standby replicas and configure quorum replication (
ANY 1 (node1, node2)) so that either replica can satisfy commits if the other goes offline. - Utilize automated failover/monitoring daemons (such as Patroni) to automatically adjust
synchronous_standby_namesif node counts fall below quorum.
Exam Tips and Common Pitfalls
- Exam Trap: Read-Your-Writes Consistency: If an exam scenario describes an application that writes to a primary database, immediately reads from a standby replica, and gets stale data, the default setting
synchronous_commit = onis not sufficient to prevent this! The settingsynchronous_commit = ononly waits for disk flush (flush_lsn), not replay. The only setting that guarantees instant query visibility on the replica issynchronous_commit = remote_apply. - Exam Trap: Granular Overrides: You do not have to run an entire cluster in synchronous or asynchronous mode. You can leave
synchronous_commit = onglobally for financial transactions, and setsynchronous_commit = offorlocalinside specific batch scripts or sessions to maximize bulk loading throughput. - Exam Trap: Quorum vs Priority Syntax: In
synchronous_standby_names, the keywordANYdenotes quorum (any $N$ nodes satisfy the commit), whereasFIRSTdenotes strict priority order (the first $N$ reachable nodes in the listed order satisfy the commit).
A web application executes an e-commerce order checkout on the primary database, immediately redirects the customer to an order summary page that reads from a hot standby replica, and frequently displays an empty 'Order Not Found' page due to replication delay. Which configuration change guarantees that the standby replica has applied the transaction to its buffer pages before the primary returns success to the client?
An administrator manages a two-node PostgreSQL cluster with synchronous_standby_names = 'standby1' and synchronous_commit = on. If the server hosting standby1 suffers a sudden hardware failure and powers off, what will happen to ongoing and new client transactions on the primary server?
A high-availability cluster consists of one primary and three standbys (node1, node2, node3). The primary is configured with synchronous_standby_names = 'ANY 2 (node1, node2, node3)'. What is the status of the connected replicas in pg_stat_replication, and what condition must be met to confirm a synchronous commit?