Synchronous and Asynchronous Replication

The fundamental difference between synchronous and asynchronous replication is:

What must happen on another node before the database tells the client that the transaction succeeded?

A simplified model is:

``text id="cgayks" SYNCHRONOUS transaction | v local commit work | v required remote progress | v SUCCESS to client `

versus:

`text id="jsh4qs" ASYNCHRONOUS transaction | v local commit work | v SUCCESS to client | +------ remote replicas continue catching up `

The key difference is therefore not:

`text id="tsn6u7" "Are changes transmitted immediately?" `

but:

`text id="z4d3ow" "Does client-visible commit success depend on replica progress?" `

An asynchronous system can begin transmitting changes almost immediately.

It is still asynchronous if the client does not have to wait for replica progress before receiving success.

Start with one transaction

Suppose the application creates an order:

`sql id="oxq27b" BEGIN; INSERT INTO orders(order_id, customer_id, amount) VALUES (104, 27, 89.00); COMMIT; `

Assume:

`text id="kdjqwz" Primary Replica `

The database must decide at what point:

`text id="cjymc9" COMMIT `

is allowed to return:

`text id="wsr84l" SUCCESS `

to the application.

There are several possible milestones.

The replication pipeline

A useful generic pipeline is:

`text id="g7zvd8" PRIMARY transaction executes | v change/log record created | v local log write | v local durable flush | v send across network | v REPLICA receive | v write | v durably flush | v apply / replay | v visible to replica query `

Not every database exposes exactly these stages, but the model is extremely useful.

The important events are different:

`text id="rzqlr9" received ≠ durable ≠ applied ≠ visible to reads `

Why the acknowledgment point matters

Suppose a replica says:

`text id="11apt3" "I received transaction 104." `

That might mean only:

`text id="6rfkk6" the bytes reached my process `

If that machine immediately loses power, perhaps the change disappears.

A stronger acknowledgment is:

`text id="dyvrde" "I flushed transaction 104 to durable storage." `

Now the transaction has survived more failure modes.

An even stronger acknowledgment might be:

`text id="djj75p" "I replayed transaction 104 and my queries can now see it." `

Each additional requirement generally increases:

`text id="bq5rua" durability or visibility guarantees `

but also increases:

`text id="qjmngk" commit latency `

because the primary must wait longer.

Asynchronous replication

In asynchronous replication, replica progress is not part of the client-visible commit requirement.

Timeline:

`text id="286t9h" Client Primary Replica | | | |---- INSERT ----->| | | | | | | local durable commit | | | | |<---- SUCCESS ----| | | | | | |------- changes ----->| | | | | | receive | | persist | | apply `

The client may hear:

`text id="0zo99d" SUCCESS `

while the replica still lacks the transaction.

This produces:

`text id="4b17eq" lower commit latency `

but creates a window during which:

`text id="8374rk" primary contains newer acknowledged data than replica `

"Asynchronous" does not mean "send everything later"

This is an important correction to a common misconception.

An asynchronous primary might stream changes while the transaction is still progressing.

For example:

`text id="b4f3hu" Primary Replica generate log record 1 -----> receive generate log record 2 -----> receive generate commit record ----> receive `

The replica may even be almost completely caught up by the time the primary commits.

The replication is still asynchronous if:

`text id="y8y1l9" primary does not need a replica acknowledgment before reporting success `

So:

Synchronous versus asynchronous describes the commit dependency, not necessarily when network transmission begins.

Failure window in asynchronous replication

Suppose the sequence is:

`text id="ydrsv6" 1. Transaction 104 commits on primary. 2. Primary returns SUCCESS. 3. Transaction 104 has not reached replica. 4. Primary suffers unrecoverable failure. 5. Replica is promoted. `

The new primary may not contain:

`text id="x8gxx1" transaction 104 `

even though the application previously received:

`text id="9lxg1t" SUCCESS `

This is the classic asynchronous replication data-loss window.

Replication lag

The distance between primary and replica progress is called replication lag.

Suppose:

`text id="qbwc42" Primary: transactions through 10,000 Replica: transactions through 9,970 `

The replica is:

`text id="dxc5ti" 30 transactions behind `

But lag can be measured in several ways:

`text id="p7u8wf" bytes log positions number of transactions estimated seconds apply queue size `

A single:

`text id="iq3x27" lag = 2 seconds `

number can hide important details.

Lag in seconds can be misleading

Consider two databases.

Database A

`text id="fw8p3s" 2 seconds behind primary generating 1 KB/sec `

Outstanding replication data is tiny.

Database B

`text id="dev7bm" 2 seconds behind primary generating 5 GB/sec `

Outstanding replication work is enormous.

Both display:

`text id="vtsjkt" 2 seconds lag `

but operationally they are radically different.

That is why production monitoring should inspect:

`text id="w0xkfj" log positions bytes queued apply rates `

as well as time-based lag estimates.

Why asynchronous replicas return stale reads

Suppose:

`text id="uwb5l6" Primary: order 104 exists Replica: order 104 has not been applied ``

A user creates order 104:

``text id="k7yywv" POST /orders → Primary → SUCCESS `

Then immediately:

`text id="7f7g52" GET /orders/104 `

is routed to the replica.

The replica returns:

`text id="5oxt0v" not found `

The database has not necessarily malfunctioned.

The read simply arrived before replication caught up.

Read-your-writes consistency

Users often expect:

`text id="n18pgo" I wrote X. Then I read X. Therefore I should see X. `

That property is often called:

`text id="758v7m" read-your-writes consistency `

Plain asynchronous replicas do not automatically provide it.

Possible application strategies include:

`text id="m1o1k0" write to primary then temporarily read from primary `

or:

`text id="xeo8bq" return the newly created object directly from the write response `

or:

`text id="jrjeww" record the write's replication position and wait until the chosen replica reaches it `

The correct solution depends on the application.

Synchronous replication

Synchronous replication changes when the client receives success.

Conceptually:

`text id="t8cdrb" Client Primary Replica | | | |---- write ------>| | | | | | |------ change ------->| | | | | | required | | progress | | | | |<------- ACK ---------| | | | |<---- SUCCESS ----| | `

The primary's commit is now dependent on a particular remote event.

But:

`text id="3cmnfk" remote event `

must be defined.

"Synchronous" is incomplete without the acknowledgment level

Consider these possible requirements:

`text id="yu9v83" A. Replica received the data. B. Replica wrote it to its operating-system buffers. C. Replica durably flushed it to storage. D. Replica applied/replayed it. E. Replica made it visible to user queries. `

Each one is stronger than a simple:

`text id="05er6y" "some network packet arrived" `

Different database engines expose different choices.

Therefore:

Never document a system merely as "synchronous. " Document what acknowledgment is required.

Receive versus durable persistence

Suppose the replica receives:

`text id="aal5vf" transaction 104 `

into RAM.

It acknowledges.

Then:

`text id="qfk98e" replica power fails `

If the change had not reached durable storage, it may disappear.

So:

`text id="kg50n9" receive acknowledgment `

and:

`text id="t1op91" durability acknowledgment ``

provide different guarantees.

Durable versus applied

Now suppose the replica has safely written transaction 104 to its recovery log.

Its state is:

``text id="p42xqt" log contains transaction 104 database pages/read state still reflect transaction 103 `

If the replica crashes and restarts, it can replay the durable log.

So the transaction is protected.

But an immediate query may still not see it.

Therefore:

`text id="uh1fb8" durability `

and:

`text id="vjx79p" read freshness `

are separate properties.

The most important replication distinction

Remember:

`text id="ajqn8d" DURABLE ON REPLICA does not necessarily mean VISIBLE ON REPLICA `

That one distinction explains a surprising amount of behavior in:

`text id="v819m9" PostgreSQL SQL Server MySQL other log-based replication systems `

PostgreSQL: the clearest example

Current PostgreSQL exposes several commit levels through:

`text id="b37569" synchronous_commit `

Relevant values include:

`text id="1a9l55" remote_apply on remote_write local off ``

When synchronous standbys have actually been configured, those values determine how much standby progress the transaction waits for. PostgreSQL documents remote_write, on, and remote_apply as distinct remote acknowledgment levels.

PostgreSQL requires a synchronous standby configuration too

A subtle but important point:

``conf id="nyywqd" synchronous_commit = remote_apply `

alone does not magically create a synchronous replica.

The primary also needs:

`conf id="00oc6z" synchronous_standby_names = '...' ``

to identify which connected standbys participate in synchronous replication.

PostgreSQL's current documentation states that synchronous_standby_names must be non-empty before streaming replication becomes synchronous.

So there are two distinct questions:

``text id="mawgpi" Which replicas count? What progress must they report? ``

PostgreSQL remote_write

With:

``conf id="5jk0f6" synchronous_commit = remote_write ``

the primary waits until the selected synchronous standby reports that the commit record has been received and written to the standby operating system.

It does not require that the WAL be durably flushed to persistent storage there.

PostgreSQL therefore documents remote_write as weaker than normal synchronous on, but potentially lower latency.

Conceptually:

``text id="rlkrup" Primary durable Standby received Standby OS write completed → client SUCCESS ``

PostgreSQL synchronous_commit = on

With a synchronous standby selected and:

``conf id="85ssgn" synchronous_commit = on `

the primary waits for the synchronous standby to report that the commit WAL has been flushed to durable storage.

Conceptually:

`text id="62hmhd" Primary durable Standby received Standby durable → SUCCESS ``

PostgreSQL describes this as the normal durable synchronous-replication level.

But the standby might still not have replayed the transaction.

PostgreSQL remote_apply

With:

``conf id="3k9bxz" synchronous_commit = remote_apply `

the primary waits until the synchronous standby reports that it has:

`text id="y4mlyw" received flushed and replayed `

the transaction.

PostgreSQL specifically says that at this point the transaction has become visible to queries on that standby.

Timeline:

`text id="1quv73" Primary commit | v send WAL | v standby receive | v standby durable flush | v standby replay | v ACK | v SUCCESS ``

This provides a stronger read-after-commit property at the cost of higher latency.

PostgreSQL summary

synchronous_commit Client success waits for
remote_write Remote filesystem write
on Remote durable WAL flush
remote_apply Remote replay / read visibility
local Local durable WAL only
off Does not wait for local WAL flush before reporting success

For the remote modes to have their remote meaning, synchronous_standby_names must select synchronous standbys. PostgreSQL otherwise treats the non-off options as local durable commit behavior.

PostgreSQL can choose which standbys count

Suppose:

``text id="xuzpgv" Primary A B C `

A priority configuration might say:

`conf id="bn7ht8" synchronous_standby_names = 'FIRST 1 (a, b, c)' `

meaning:

`text id="ydhoif" wait for one synchronous standby, preferring A, then B, then C `

A quorum configuration might say:

`conf id="8rvhn6" synchronous_standby_names = 'ANY 2 (a, b, c)' `

meaning:

`text id="0im5qs" wait for acknowledgments from any two candidates `

PostgreSQL supports both priority-based and quorum synchronous replication.

Why synchronous replication increases commit latency

Suppose local storage commit takes:

`text id="8q3jvi" 1 ms `

but the required replica is:

`text id="dc7olp" 40 ms network round trip away `

The commit must now include enough remote communication to obtain the acknowledgment.

Conceptually:

`text id="19rcnm" local work 1 ms network/storage 40+ ms ---------------------- commit latency much higher `

Actual latency is pipeline-dependent, so these numbers should not simply be added mechanically.

The important point is:

Remote coordination is now on the transaction's critical path.

Geography therefore matters

A synchronous replica in the same availability zone or nearby datacenter may have low round-trip latency.

A required synchronous replica on another continent can dramatically increase write latency.

This often produces architectures such as:

`text id="n86xvo" Primary | +-- nearby synchronous standby | +-- remote asynchronous disaster-recovery standby `

Different copies have different purposes.

MySQL uses different terminology: semisynchronous replication

Traditional MySQL source-to-replica replication is asynchronous.

MySQL 8. 4 also supports:

`text id="51f68w" semisynchronous replication `

The source waits for acknowledgment from one or more configured semisynchronous replicas before returning to the committing session.

The name is intentional:

`text id="b2gv86" semisynchronous `

rather than simply:

`text id="otjo4r" fully synchronous apply `

because replica application remains separate.

What does a MySQL semisynchronous replica acknowledge?

MySQL documents that the replica acknowledges a transaction after its events have been written to the replica's relay log and flushed to disk.

Conceptually:

`text id="xphw76" SOURCE transaction | binary log | +--------------------> REPLICA | relay log | durable flush | ACK <----------------------+ | client may continue `

The replica does not have to finish applying the transaction before sending that semisynchronous acknowledgment.

MySQL semisynchronous durability is not replica read freshness

Immediately after the acknowledgment:

`text id="hvh6ab" Replica relay log: contains transaction 104 `

but:

`text id="ksnbat" Replica SQL/applier: may not have applied 104 yet `

Therefore:

Before WAL has been applied on the replica, its relevant rows may look like this:

id total
103 42.00

`sql id="hdd8fn" SELECT * FROM orders WHERE id = 104; `

rows returned
0

The query on the replica may still lag behind. After WAL applies the new row, the result becomes:

id total
104 89.00

Again:

`text id="ik5330" durable replication progress ≠ query-visible application ``

MySQL AFTER_SYNC

MySQL's default semisynchronous source wait point is:

``text id="xf1qft" AFTER_SYNC `

At a high level:

`text id="cprj66" source writes transaction to binary log source sends it toward replicas source syncs binary log source waits for required replica acknowledgment source commits to storage engine source returns result ``

MySQL documents AFTER_SYNC as the default semisynchronous wait point.

This ordering is specifically chosen to improve consistency of what clients can observe on the source during failover scenarios.

MySQL AFTER_COMMIT

The alternative is:

``text id="y36fd3" AFTER_COMMIT `

Conceptually:

`text id="8mcs6d" source writes/syncs binary log source commits locally source waits for replica acknowledgment source returns SUCCESS ``

So the remote acknowledgment still affects when the client hears success, but it occurs after local storage-engine commit.

MySQL documents both AFTER_SYNC and AFTER_COMMIT as supported semisynchronous wait points.

MySQL semisynchronous can fall back to asynchronous

This is an extremely important operational detail.

Suppose the source requires a replica acknowledgment but no qualifying replica responds.

MySQL waits up to:

``text id="6xosjk" rpl_semi_sync_source_timeout `

The default documented value is:

`text id="t490r8" 10,000 ms `

After that timeout, MySQL can revert to ordinary asynchronous replication.

So:

`text id="8qp5qe" semisynchronous enabled `

does not necessarily mean:

`text id="608mex" the source will refuse all future writes until a replica acknowledges. `

The configured fallback behavior matters.

This changes the durability guarantee

Suppose semisynchronous replication is healthy:

`text id="dsa1ay" Source | +----> Replica ACK `

Now the replica disappears.

After the timeout:

`text id="u82d08" source falls back to asynchronous mode `

The application may continue receiving successful commits.

Those newer commits do not have the same remote durability guarantee.

Therefore monitoring should distinguish:

`text id="55p5cm" semisynchronous configured `

from:

`text id="qpu234" semisynchronous currently operational `

MySQL exposes status variables specifically for that purpose.

MySQL can require several acknowledgments

The setting:

`text id="q5g6m4" rpl_semi_sync_source_wait_for_replica_count `

controls how many replica acknowledgments are required.

Its documented default is:

`text id="v1lm6u" 1 `

For example:

`text id="061539" source | +--> replica A +--> replica B +--> replica C `

with:

`text id="svua8c" wait_for_replica_count = 2 `

the source waits for two replica acknowledgments before satisfying the semisynchronous requirement.

Waiting for application is separate in MySQL too

MySQL provides synchronization functions that can wait until a replica has applied changes through a specified source binary-log position.

For example, current MySQL provides:

`text id="w2xaqu" SOURCE_POS_WAIT() `

which waits until a replica has read and applied updates through a requested position.

This is a different operation from semisynchronous commit acknowledgment.

Semisynchronous acknowledgment answers:

`text id="e966me" has enough remote durability been achieved? `

whereas position waiting can answer:

`text id="xq702c" has this replica applied through the position I need to read? `

SQL Server Availability Groups

SQL Server Always On Availability Groups provide:

`text id="hotd83" asynchronous-commit mode `

and:

`text id="fqzmfk" synchronous-commit mode `

For an asynchronous secondary, the primary does not wait for that secondary to harden its transaction log before committing. Microsoft documents that this minimizes transaction latency but permits lag and possible data loss if failover uses a secondary that has not caught up.

SQL Server synchronous commit

For a healthy synchronous-commit secondary:

`text id="ocqvpq" Primary | send transaction log records | v Secondary | harden log | ACK | v Primary completes commit `

Microsoft explicitly describes the acknowledgment as the secondary confirming that it has hardened the log.

That is a durability milestone.

It is not yet necessarily a read-visibility milestone.

SQL Server redo is separate

After the secondary hardens its transaction log:

`text id="tn6odb" transaction is durable in secondary log `

SQL Server still runs redo to apply those log records to the secondary's database files.

Conceptually:

`text id="x5qllq" receive | v harden transaction log | v redo | v query-visible database state `

If redo cannot keep up, a:

`text id="vgv91l" redo queue `

develops. Microsoft documents this explicitly.

SQL Server synchronous secondary reads can therefore be stale

Suppose:

`text id="r5ja48" log hardened through LSN 500 redo applied through LSN 450 `

A transaction at:

`text id="6v1q9q" LSN 480 `

is durably present on the secondary.

But a read-only workload there may still not see its effects until redo catches up.

Microsoft specifically warns that readable secondaries can return stale data when hardened log records are waiting in the redo queue.

So:

`text id="td512s" SQL Server synchronous commit `

does not mean:

`text id="9cq3mv" every readable secondary is caught up for queries. `

SQL Server has an important failure nuance

A simplistic table often says:

`text id="bcew57" synchronous replica disappears → primary blocks forever `

That is not universally correct for SQL Server Availability Groups.

If a synchronous secondary stops responding long enough, SQL Server can mark it:

`text id="7kgidf" NOT SYNCHRONIZING ``

and the primary can proceed rather than waiting for it under the normal/default behavior.

This means the data-protection state can change when the secondary becomes unhealthy.

REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT

SQL Server 2017 and later provide:

``text id="4e9543" REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT `

This lets administrators specify a minimum number of synchronized secondaries that must be available for primary commits.

For example:

`text id="fwajx6" required = 1 `

can cause the primary to stop accepting normal commits if it cannot maintain the required synchronized-secondary condition.

Microsoft explicitly describes this as trading primary availability for stronger data protection.

This is an excellent example of why:

`text id="3r15kg" "synchronous mode" ``

by itself is not the complete configuration.

Technology comparison

Technology Async mode Stronger mode What remote acknowledgment means
PostgreSQL Streaming replication normally async Synchronous standby remote_write, durable flush with on, or replay with remote_apply
MySQL Traditional source/replica async Semisynchronous replication Required replica has durably logged transaction events in relay log
SQL Server AG Asynchronous commit Synchronous commit Secondary hardened transaction log
SQL Server with required synchronized secondaries — Stronger availability/data-protection rule Minimum configured number must remain synchronized for commit availability

The important differences are documented by PostgreSQL's synchronous-replication settings, MySQL's semisynchronous replication protocol, and SQL Server Availability Group modes.

Do not confuse replication mode with local durability

There are actually two separate questions:

``text id="oxzv4o" 1. Has the transaction become durable on the local primary? 2. Has it reached the required state on another replica? `

For example, PostgreSQL:

`conf id="7t4i2i" synchronous_commit = off `

can report a transaction success before its WAL has been durably flushed locally.

That is a local durability choice, not simply:

`text id="w63ehh" asynchronous replication ``

PostgreSQL documents off separately from the remote synchronous modes.

Similarly, SQL Server supports delayed transaction durability independently of Availability Group replication.

A useful two-axis model

Think of:

``text id="h8cgx7" LOCAL DURABILITY `

and:

`text id="7gf5dv" REMOTE REPLICATION `

as separate dimensions.

For example:

Local primary Replica Meaning
Durable No acknowledgment required Traditional async replication
Durable Replica receives/logs Semisynchronous-style guarantee
Durable Replica durably flushes Strong synchronous durability
Durable Replica applies Strong synchronous visibility
Not yet durable No remote requirement Delayed local durability; weakest crash guarantee

This prevents terminology such as:

`text id="1v2a92" sync / async `

from hiding several independent choices.

What if the replica fails?

With asynchronous replication:

`text id="le6r68" Primary --------X-------> Replica `

the primary normally continues committing.

The replica simply falls further behind.

This favors:

`text id="3zd7k5" write availability `

but reduces:

`text id="bqr0e2" redundant durability `

until replication recovers.

What if a required synchronous replica fails?

Now suppose the primary truly requires a remote acknowledgment.

The system must choose one of several policies:

`text id="1gk5ph" wait indefinitely wait for another eligible synchronous replica fail the write stop write availability temporarily degrade to asynchronous behavior ``

Different products/configurations choose differently.

PostgreSQL synchronous transactions can remain waiting when the required synchronous-standby condition cannot be satisfied. PostgreSQL recommends configuring enough candidate synchronous standbys to maintain HA.

MySQL semisynchronous replication, by contrast, can time out and fall back to asynchronous operation.

SQL Server's behavior depends additionally on synchronization health and REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT.

So "synchronous is less available" needs qualification

A common statement is:

``text id="fswfws" synchronous replication = replica failure blocks writes `

That can be true.

But the accurate version is:

A strict remote acknowledgment requirement creates an availability dependency on the replicas needed to satisfy that requirement.

If a technology automatically degrades to asynchronous operation:

`text id="4zsezm" writes may remain available `

but:

`text id="84rol6" the durability guarantee has weakened. `

So always ask:

`text id="k0ou7e" What does the database do when the required replica disappears? `

Durability versus availability during a partition

Imagine:

`text id="l877lb" Primary X synchronous replica `

The network is broken.

The primary can choose:

Preserve remote durability guarantee

`text id="marg4v" do not acknowledge new writes `

Availability suffers.

Preserve write availability

`text id="2lovge" continue local commits `

Remote durability guarantee is weakened.

This is another concrete manifestation of distributed-systems trade-offs.

Failover requires more than synchronous replication

Suppose:

`text id="c6bh0k" Primary fails. Replica has every acknowledged transaction. `

Excellent.

But the system still has to:

`text id="h24mps" detect the failure choose the new writer promote/elect it prevent the old primary from accepting writes redirect clients `

Synchronous replication solved:

`text id="qeriy0" where the transaction existed `

not:

`text id="5add41" who should now own write authority. `

Therefore synchronous replication and failover are separate concerns.

Synchronous replication does not automatically prevent split brain

Suppose the old primary becomes isolated.

A standby is promoted.

Then the old primary reconnects only to clients and still believes it is writable.

Now:

`text id="vsfdvn" Old primary: accepts X New primary: accepts Y `

There are two write histories.

This is split brain.

Preventing it requires:

`text id="t8e09e" quorum fencing leases cluster coordination or another ownership mechanism `

depending on the technology.

Replication acknowledgment alone is not enough.

Which replica should be promoted?

Suppose three async replicas exist:

`text id="xfhjg0" A applied through 1000 B applied through 995 C applied through 970 `

Primary fails.

Choosing C merely because:

`text id="7i60fa" C responded first `

could lose more data than promoting A.

A failover manager therefore needs to understand:

`text id="vu90x6" replication position durability state health topology authority `

Promotion policy is part of the consistency/durability architecture.

RPO: Recovery Point Objective

RPO answers:

How much committed business data may be lost after a disaster?

Examples:

`text id="m7um9h" RPO = 0 `

means:

`text id="td97bm" no acknowledged transactions may be lost for the failures included in the design. `

An RPO of:

`text id="lgiijw" 5 minutes `

means the business accepts recovery potentially losing up to approximately five minutes of recent data under the specified disaster scenario.

Replication design should follow the RPO requirement.

Not the reverse.

RPO is not simply "replication lag"

Suppose normal lag is:

`text id="02hd6v" 200 ms `

That does not automatically mean:

`text id="ty9fef" RPO = 200 ms `

RPO is a business/recovery objective.

Actual potential loss depends on:

`text id="v46lhu" replication mode failure type which replicas survive which replica is promoted whether remote data is durable `

Monitoring lag helps determine whether the implementation is meeting the intended RPO.

RTO: Recovery Time Objective

RTO answers:

How long can the service remain unavailable after failure?

Suppose:

`text id="pvs777" primary fails at 12:00:00 `

and:

`text id="b584tn" service accepts writes again at 12:00:25 `

recovery took:

`text id="6icqbk" 25 seconds `

RTO therefore concerns:

`text id="8futs3" failure detection election/promotion recovery routing application reconnection `

not merely replication.

Apply lag can hurt RTO too

Suppose a synchronous replica has every transaction durably recorded.

Excellent RPO.

But it has:

`text id="z3zx3e" 200 GB `

of unapplied recovery/redo work.

Failover may still require additional recovery work before the new database becomes fully usable.

Microsoft explicitly documents that a large SQL Server redo queue can extend failover time even when the log has already been hardened on the secondary.

Thus:

`text id="rbt5kj" good RPO `

does not automatically imply:

`text id="53sumo" good RTO. `

RPO and RTO are independent dimensions

Imagine two systems.

System A

`text id="890v8k" RPO = 0 RTO = 2 hours `

No committed data is lost, but recovery is slow.

System B

`text id="o80f6t" RPO = 5 minutes RTO = 15 seconds `

It comes back quickly but may lose several minutes of work.

Neither is inherently better.

The correct design depends on business requirements.

Synchronous replication is not backup

Suppose the application executes:

`sql id="jov6ak" DELETE FROM orders; `

Synchronous replication can ensure that the deletion is durably copied to another server before the primary reports success.

Now both contain:

`text id="q60fvk" orders deleted `

The replication system worked perfectly.

It did not preserve the old data.

The same is true for logical corruption

Examples:

`text id="0xm8uy" bad deployment updates wrong rows administrator drops table application writes corrupted values attacker with valid DB permissions deletes data `

A replica faithfully copying current database state may reproduce every one of those changes.

You still need:

`text id="j1t4i3" backups WAL/binlog/log retention point-in-time recovery restore testing `

for historical recovery.

Monitoring must distinguish the pipeline stages

A good monitoring model asks:

`text id="1scbpd" How far has the primary generated? How far has the primary sent? How far has the replica received? How far has it durably persisted? How far has it applied? How far behind is the query-visible state? `

Different stages imply different operational failures.

Example monitoring diagnosis

Suppose:

`text id="jb5a5w" Primary position: 10000 Replica received: 9998 Replica durable: 9998 Replica applied: 8000 `

Network transport is healthy.

Durability is nearly current.

But application/replay is badly behind.

Likely investigation area:

`text id="541r3w" replica CPU disk I/O long-running queries apply-thread capacity redo/replay blocking `

Adding network bandwidth would probably not solve that problem.

Another diagnosis

Now suppose:

`text id="hn38y2" Primary position: 10000 Replica received: 7000 Replica durable: 7000 Replica applied: 6999 `

Apply is keeping pace with what arrives.

The big gap is before receipt.

Investigate:

`text id="s8uut5" network sender throughput receiver throughput replication connection bandwidth `

Same high-level symptom:

`text id="hxczl1" replica behind `

different cause.

PostgreSQL monitoring example

PostgreSQL exposes:

`sql id="y6ot0u" SELECT application_name, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag FROM pg_stat_replication; `

Example output for one replica (lag intervals and WAL positions are illustrative):

application_name state sync_state sent_lsn write_lsn flush_lsn replay_lsn write_lag flush_lag replay_lag
standby1 streaming async 0/5000100 0/5000100 0/5000100 0/4FFFE00 00:00:00.050 00:00:00.050 00:00:00.250

The positions correspond to increasingly advanced stages of standby progress.

This is far more informative than a single:

`text id="9xwgsn" replica connected = yes `

flag.

SQL Server monitoring example

SQL Server Availability Group monitoring includes concepts such as:

`text id="i7ceeb" log send queue redo queue redo rate synchronization state `

If:

`text id="38tuzm" log send queue large `

the secondary has not yet received/hardened all the primary's log.

If:

`text id="hcc0pg" redo queue large `

the log may already be safely on the secondary but has not yet been applied to query-visible data. Microsoft explicitly distinguishes these states in its Availability Group diagnostics.

MySQL monitoring also needs two stages

For traditional MySQL replication, distinguish:

`text id="wlj07m" receiver progress `

from:

`text id="qq7x41" applier progress `

The replica may have received transactions into its relay log while the SQL/applier side remains behind.

GTID sets or binary-log positions provide more useful progress information than a simple:

`text id="3mfhuw" replica process running `

status.

Synchronous replication and throughput

Synchronous replication does not necessarily reduce raw transaction throughput by exactly:

`text id="0nccu4" 1 / network latency `

because databases pipeline:

`text id="1jjfvl" WAL writes network transmission group commit replica acknowledgments `

across concurrent transactions.

A system can still process many synchronous commits concurrently.

However, individual transaction latency must include enough remote progress to satisfy the synchronization rule.

So the practical impact depends on:

`text id="g3k5bt" network RTT storage latency group commit transaction rate replica performance number of required acknowledgments `

Group commit helps

Suppose 100 transactions become ready to commit at nearly the same time.

A database may avoid doing:

`text id="rm5c64" 100 independent disk syncs `

and instead combine durability work.

Conceptually:

`text id="u5vxll" T1 T2 T3 ... T100 \ +---- one batch WAL/log flush `

Similar batching can help replication.

Therefore synchronous replication can still provide substantial throughput even while adding remote commit latency.

Synchronous replication and tail latency

Average latency is not the only concern.

Suppose:

`text id="hotgha" replica normally acknowledges in 2 ms `

but occasionally stalls for:

`text id="f1enl1" 200 ms `

If that replica is required for commit, user transaction latency may suddenly spike.

This produces high:

`text id="1od2wk" p95 p99 p99.9 `

latency even if average performance looks acceptable.

For user-facing workloads, monitor latency distributions rather than averages only.

One slow synchronous participant can matter greatly

Suppose the primary waits for:

`text id="6gw5gg" ALL 3 replicas `

and their response times are:

`text id="4omqph" A = 2 ms B = 3 ms C = 100 ms `

The slow replica may dominate the acknowledgment.

If the policy instead requires:

`text id="hi5xma" ANY 2 `

the transaction can potentially proceed after A and B respond.

This is why quorum/standby-selection policy has major performance consequences.

More synchronous copies are not free

Suppose the durability goal changes from:

`text id="mre1j7" wait for one remote durable copy `

to:

`text id="vyc353" wait for three remote durable copies `

The system may gain resilience against more simultaneous failures.

But it may also increase exposure to:

`text id="qwfzys" slow replicas network jitter storage stalls partial outages `

Replication count should follow the failure model and business requirement.

A practical architecture example

Suppose an online payment company deploys:

`text id="iax7ys" Frankfurt Primary Frankfurt Standby A Berlin Standby B Virginia Standby C `

One possible policy:

`text id="251g05" Standby A: required synchronous durable copy Standby B: synchronous candidate/failover Standby C: asynchronous disaster-recovery copy `

Why?

`text id="00k70d" A/B: low enough latency for synchronous commits C: far away; synchronous waiting would add too much latency `

The topology balances:

`text id="h3c3tv" RPO latency regional resilience `

rather than treating every replica identically.

Another architecture: analytics replica

Suppose:

`text id="som09q" Primary: OLTP Replica A: HA Replica B: analytics `

Replica B runs:

`text id="4hu32t" large joins hour-long reports full scans ``

It may deliberately be asynchronous because analytics freshness requirements allow lag.

Replica A, meanwhile, may use stricter synchronous behavior because it is a failover candidate.

This illustrates an important principle:

Different replicas can have different jobs and therefore different replication guarantees.

Comparison table

Concern Synchronous Asynchronous
Client commit Waits for configured remote progress Does not wait for replica progress
Typical latency Higher Lower
Dependency on remote replica Stronger Weaker
Replica lag Can still exist after the acknowledged stage Expected
Read freshness Depends on whether apply/replay is included Usually may be stale
Primary failure Required durable copies can protect acknowledged commits Recent acknowledged commits may be missing
Network partition Strict requirement may stop commits Primary may continue alone
Geographic deployment WAN latency can affect commit Remote copies can lag without delaying commit
Failover Still requires promotion/election and fencing Same, plus potential-loss decision
Historical recovery Still needs backup/PITR Still needs backup/PITR

The table is a starting point, not a substitute for knowing a product's exact acknowledgment and fallback rules.

Technology-specific summary

Engine Mode Commit dependency
PostgreSQL Async streaming No standby required
PostgreSQL remote_write Required standby has written WAL to OS
PostgreSQL on with sync standby Required standby durably flushed WAL
PostgreSQL remote_apply Required standby replayed commit
MySQL Async replication No replica required
MySQL Semisynchronous Configured replica count durably logged events before client proceeds
SQL Server AG Async commit No secondary harden acknowledgment required
SQL Server AG Sync commit Healthy synchronous secondary hardens transaction log
SQL Server AG Required synchronized secondaries Configured minimum synchronization requirement can gate primary commit availability

This demonstrates why:

``text id="6ums28" sync `

and:

`text id="36fn8a" async `

are families of behavior, not complete specifications.

Common misconceptions

Claim Better explanation
"Async sends changes only after commit. " Transmission can overlap; the client simply does not wait for replica progress
"Sync means every replica is identical at commit. " Only the required replicas/stage must meet the configured acknowledgment condition
"Replica received it, so it is safe. " Receipt may differ from durable persistence
"Replica flushed it, so queries can see it. " Apply/replay may still lag
"Sync replication guarantees fresh reads everywhere. " Read freshness depends on which replica and acknowledgment/apply policy
"Sync replication guarantees zero data loss. " Only for the covered failure model and correct failover target
"Async always loses data on failure. " It can lose recent acknowledged changes; whether loss actually occurs depends on timing
"Synchronous secondary failure always blocks the primary. " Product/fallback policy matters
"Semisync MySQL means replica applied the transaction. " The acknowledgment concerns durable relay-log receipt, not completed application
"Replication gives us backup. " Bad changes replicate too

Questions to ask when someone says "we use synchronous replication"

Ask:

`text id="4r46se" Which replicas participate? How many acknowledgments are required? What does acknowledgment mean? Received? Written? Durably flushed? Applied? Visible? What happens if one required replica disappears? Do writes block? Fail? Choose another replica? Fall back to asynchronous? Which node can be promoted? How is split brain prevented? Can reads come from a replica? How stale may they be? `

Until those questions are answered:

`text id="yh3uiv" "We use synchronous replication" `

does not fully describe the guarantee.

Questions to ask for asynchronous replication

Likewise:

`text id="mk93nq" How far may replicas lag? How do we measure lag? Which reads can use replicas? How do we provide read-your-writes? Which replica is selected for failover? What acknowledged data might it be missing? What RPO does the business permit? How long can a replica remain behind before it must be rebuilt? How is WAL/binlog/log history retained? `

Asynchronous replication is not:

`text id="k805fw" "bad replication" `

It is a deliberate latency/availability trade-off.

Choosing based on the business operation

Consider several workloads.

Social-media like counter

A small amount of temporary replica staleness may be harmless.

Asynchronous replication can be a good fit.

Financial transfer ledger

Losing an acknowledged transaction may be unacceptable.

Stronger synchronous durability may be appropriate.

Analytics

Freshness within:

`text id="o9qxnc" 30 seconds `

may be perfectly adequate.

An asynchronous analytical replica may make sense.

User creates profile, then immediately loads it

General replica lag may be acceptable, but this specific interaction requires:

`text id="83cz3s" read-your-writes ``

So the application might temporarily route the user's reads to the primary.

Different operations in the same application may need different guarantees.

PostgreSQL can choose durability per transaction

PostgreSQL's synchronous_commit can be changed at transaction/session level.

That means one application could choose:

``text id="kao36n" payment transaction: remote_apply / on `

while another less critical transaction uses:

`text id="bw9xnn" local or off `

under an appropriate architecture.

PostgreSQL explicitly supports per-transaction synchronous-commit choices.

This can avoid imposing the highest durability cost on every piece of data.

Measure the workload, not only the configuration

Before changing replication mode, measure:

`text id="4jmr5m" commit latency p50/p95/p99 network RTT WAL/binlog/log generation rate replica receive rate flush rate replay/apply rate replica queue size replica read staleness failure/failover time storage latency `

A configuration that looks safe on paper can perform poorly if:

`text id="cy26jv" one standby disk is overloaded `

or:

`text id="zad5qy" network latency is unstable. `

Test failures rather than only normal replication

A meaningful replication test plan should include:

Test Question answered
Normal write Does replication work?
Immediate replica read How stale can reads be?
Slow replica disk How does commit/apply latency react?
Replica disconnect Does primary block, fail, or degrade?
Network partition What happens to write availability?
Primary crash just after commit Which acknowledged transactions survive?
Failover while replica behind What data is lost?
Large apply queue What happens to RTO?
Old primary returns Is split brain prevented?
Accidental delete Can backup/PITR restore the previous state?

Replication is a failure-handling mechanism.

It should therefore be tested under failure.

RPO/RTO decision matrix

Requirement Likely architectural pressure
RPO approximately zero for covered primary failure Require durable remote acknowledgment
Very low write latency Prefer local/asynchronous path
Cross-continent replica Async often easier for latency
Immediate read from secondary Need apply-aware synchronization
Maximum write availability if replicas fail Async or degradable policy
Must stop rather than weaken durability Strict synchronous requirement
Fast failover Keep candidate caught up and monitor replay/redo
Historical recovery Separate backups/PITR

The correct mode follows the recovery objective.

Not a universal rule such as:

`text id="1epb0g" "synchronous is always better." `

Final mental model

Do not memorize:

`text id="nxjhst" synchronous = same data everywhere asynchronous = delayed copy `

Instead memorize this pipeline:

`text id="qb4iux" PRIMARY execute transaction | v local log | v local durable | v SEND | v ---------------- NETWORK ---------------- | v REPLICA RECEIVE | v REPLICA WRITE | v REPLICA DURABLE FLUSH | v REPLICA APPLY / REPLAY | v VISIBLE TO READERS `

Then place:

`text id="n05o0q" CLIENT SUCCESS `

somewhere on that timeline.

Asynchronous

`text id="qwwkhd" local durability | SUCCESS | remote replication continues `

Remote-write synchronous

`text id="jgoc2s" replica receives/writes | SUCCESS `

Durable synchronous

`text id="70c7bo" replica durable | SUCCESS `

Apply-aware synchronous

`text id="vpu73l" replica replayed | SUCCESS `

Then ask:

`text id="xsr5hs" How many replicas have to reach that point? What happens if they cannot? `

Those two questions define most of the practical replication guarantee.

Final principle

The most useful question is not:

"Is our replication synchronous or asynchronous? "

It is:

"When the application receives a successful commit, exactly which nodes possess that transaction, how durable is it on each of them, has it been applied there, and what will the system do if those nodes stop communicating at that exact moment? "

Once that question can be answered precisely, the trade-offs between:

`text id="z2ym2m" latency durability availability read freshness RPO RTO failover safety ``

become much easier to reason about.

References

PostgreSQL's current documentation distinguishes remote_write, on, and remote_apply; explains the role of synchronous_standby_names; and documents both priority-based and quorum synchronous replication.

MySQL 8. 4 documents asynchronous source/replica replication and semisynchronous replication, including durable relay-log acknowledgment, configurable acknowledgment counts, AFTER_SYNC/AFTER_COMMIT, and timeout-based fallback to asynchronous replication.

Microsoft's current SQL Server Availability Group documentation distinguishes asynchronous and synchronous commit, defines synchronous acknowledgment as secondary log hardening, and documents REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT for stronger availability/data-protection control.

Microsoft separately documents redo queuing, in which log records can already be hardened on a secondary while remaining unapplied and therefore not yet visible to readable-secondary queries.