DBRaven
Failure Mode · replication

Replica Divergence

critical

Summary

A replica in a single-leader replication topology applies changes differently than the primary did, producing a permanent state difference rather than a temporary lag. Reads from the replica return data the primary never contained, not stale data that will eventually catch up, and nothing about the replica's own error state signals that anything is wrong.

Description

This entity is scoped to single-leader (primary/replica) replication, PostgreSQL and MySQL being the canonical cases, where there is exactly one authoritative source and a replica's job is to reproduce it exactly. Divergence there is always a bug: the replica should be byte-for-byte reconstructable from the primary's change stream, and any difference is a defect to fix. Leaderless and multi-leader systems (Cassandra, DynamoDB, Riak) do not have a single authoritative source to diverge from; temporary disagreement between replicas is an expected, designed-for state there, resolved by read repair, hinted handoff, and anti-entropy (typically Merkle-tree) repair rather than by the row-based-replication and checksum tooling this entity covers. None of this entity's mitigations (binlog row format, a read-only flag, pt-table-checksum) apply to that model, which is why it is out of scope here rather than folded in.

Divergence is distinct from replication lag. Lag is temporal: the replica is correct but behind, and will catch up. Divergence is permanent: the replica holds data that does not match the primary and never will without manual intervention, because nothing in ordinary replication will overwrite it back to the correct value.

Divergence causes, in the single-leader model:

1. Non-deterministic functions in statement-based replication: RAND(), NOW(), UUID()

produce different values when the same SQL statement re-executes on the replica.

"INSERT INTO t VALUES (NOW())" inserts the primary's execution time on the

primary, but a different time on the replica if the statement itself, rather than

its resulting row values, is what gets shipped. Row-based replication eliminates

this entirely by shipping the actual row values instead of the statement.

2. Direct writes to a replica: a misrouted connection or an operator error writes to

a replica directly. That write is invisible to the primary; a later primary

update that touches the same row overwrites it, or, if no later update touches

it, the divergent value persists silently forever.

3. Non-replicated DDL or manual data manipulation: an operator runs ALTER TABLE or a

manual UPDATE directly against a replica for an "emergency fix." The replica now

has a different schema or different data with no record of why.

4. Clock skew combined with timestamp-based conflict resolution: this is specifically

a multi-leader-adjacent case (DynamoDB, Cassandra LWW) resolving concurrent writes

by comparing timestamps rather than a single-leader case, included here because

the mechanism is the same divergence-through-ambiguous-ordering that clock_skew

covers directly. If node clocks drift, "last write wins" can pick the wrong write,

since the timestamps being compared are not actually measuring true order, only

each node's own clock at write time.

5. Partial transaction application failure: in MySQL GTID replication, a transaction

that errors on the replica and gets silently skipped (sql_slave_skip_counter, or

equivalent) leaves the replica permanently missing those changes, with no error

surfaced afterward to indicate the gap.

Characteristics

Propagationisolated
Time to detectReplica divergence can persist for days or weeks undetected without active consistency verification. It typically surfaces as a user-reported inconsistency ("I see X in the app, my colleague doesn't"), a mysterious analytics discrepancy, or a checksum-comparison alert. Proactive detection requires pt-table-checksum (Percona Toolkit) or an equivalent periodic consistency check; nothing about ordinary replication monitoring catches this on its own.
Blast radiusReads from the diverged replica return incorrect data, not stale data. Depending on what diverged, the impact ranges from incorrect analytics aggregations (non-critical) to incorrect financial balances or access-control state (critical). If the primary fails and the diverged replica is promoted, the divergent data becomes the new authoritative state, and the corruption propagates to every subsequent replica of it.

Triggers

  • ·Statement-based replication with non-deterministic SQL functions
  • ·Application writes routed to a read replica (a misconfigured connection string)
  • ·Manual DML executed directly on a replica for a "quick fix"
  • ·Skipping a failed replicated transaction (sql_slave_skip_counter or equivalent) without root-causing it
  • ·Clock skew in a system using timestamp-based conflict resolution (see clock_skew)

Detection Signals

alert

Mitigation Strategies

Use row-based replication (ROW or MIXED mode)preventscomplexity: low

Configure MySQL binlog_format=ROW (PostgreSQL's WAL-based replication is row-based by default and unaffected). Row-based replication ships actual before/after row values instead of the SQL statement, eliminating non-determinism from re-executing functions on the replica entirely.

Enforce read-only on replicas at the database levelpreventscomplexity: low

Set read_only=ON (MySQL) or default_transaction_read_only=on (PostgreSQL) on every replica instance. A direct write attempt then fails at the database layer itself, not just at an application-level guard that a misrouted connection or an operator with direct access can bypass.

Periodic consistency verification with pt-table-checksumcomplexity: medium

Run pt-table-checksum (Percona Toolkit for MySQL) or pgcompare on a schedule (daily or weekly for critical tables). It computes and compares checksums per table chunk across the primary and its replicas, surfacing diverged rows as an alert rather than waiting for a user to notice.

GTID-based replication with explicit error handlingpreventscomplexity: medium

Enable GTID (Global Transaction Identifiers) in MySQL, and never use sql_slave_skip_counter to skip a failed transaction without investigating the root cause first. Every skipped transaction is a permanent, silent divergence point.

Recovery Steps

  1. 1.Run pt-table-checksum or equivalent to identify which tables and rows have diverged
  2. 2.Use pt-table-sync (Percona Toolkit) to repair diverged rows by syncing from the primary to the replica
  3. 3.Identify the root cause before repairing: repairing without fixing the cause just re-diverges
  4. 4.Enable read_only on all replicas to prevent further direct writes
  5. 5.Switch to row-based replication if statement-based replication was the cause
  6. 6.Review operator access controls on replica instances

Estimated recovery time: Divergence repair with pt-table-sync takes hours for large tables, since it processes rows in chunks to avoid overloading replication. Schema divergence from a direct ALTER TABLE on a replica requires a full rebuild from backup, potentially 4 to 48 hours for large databases. Root-cause identification and a process fix are needed before repair, or the same divergence recurs immediately.

Affected Systems

Patterns

read replicasharding

Technologies

mysqlpostgresql

Basis

Replica divergence causes and detection methods are documented in MySQL and PostgreSQL replication documentation; Percona Toolkit's pt-table-checksum is a widely used production tool; non-deterministic statement-based replication is a documented MySQL gotcha in the MySQL replication manual; the single-leader versus leaderless scope boundary follows directly from how each replication model handles disagreement between replicas.

Related Architecture Knowledge

Inbound: affects this entity

Vulnerable ToTechnology
mysql
Grounded

MySQL with statement-based binlog replication is vulnerable to replica divergence when SQL contains non-deterministic functions; row-based replication eliminates this vulnerability.

Full relationship →
Vulnerable ToWorkload
read heavy api
Grounded

Read-heavy APIs that serve reads from replicas are vulnerable to replica divergence, where the replica contains data that never existed on the primary due to non-deterministic replication.

Full relationship →