DBRaven
Failure Mode · replication

Asymmetric Replication Topology Failure

critical

Summary

When a read replica diverges from the primary by silently skipping or misapplying transactions, queries to the replica return factually incorrect data with no error, no exception, and no replication error in the logs. The divergence accumulates undetected over days or weeks until a data audit reveals the discrepancy. All reads served from the diverged replica during this window are potentially incorrect, and no automated mechanism in the replication layer detects or corrects the divergence.

Description

Statement-based replication (MySQL SBR) and row-based replication (MySQL RBR, PostgreSQL logical replication) both carry the risk of silent divergence under specific conditions. In MySQL row-based replication, the replica applies a row-change event by locating the target row using a primary key or unique key lookup and applying the delta. If the replica's row does not match what the primary expected (due to a previous incorrect application, manual modification, or software bug), the replica may: (a) silently overwrite the row with the wrong version, (b) skip the row update because it doesn't find the expected row, or (c) throw a duplicate key error and stop replication: which is the detectable failure mode. The silent cases are more dangerous.

Manual modifications on replicas are the most common cause. An operator runs a data fix directly on the replica (not the primary) to resolve an incident. This puts the replica out of sync with the primary. Subsequent replication events from the primary may conflict with the manual modification and be silently skipped (if slave_exec_mode=IDEMPOTENT in MySQL) or cause incorrect row states (if the delta is applied to the wrong base value). The operator may believe the fix was successful, not realizing that subsequent replication events are operating on incorrect state.

Software bugs in the database engine can also cause silent divergence. MySQL has had documented bugs where row-format replication events are misapplied under specific conditions (multi-column primary keys, generated columns, virtual columns, certain JOIN update patterns). These bugs are fixed in subsequent releases, but systems running older MySQL versions are exposed without knowing the replication stream has produced an incorrect replica state.

PostgreSQL logical replication is more conservative: it will stop and throw an error if a replication row cannot be applied (row not found, unique constraint violation). This makes divergence detectable but also means replication stops and manual intervention is required to resume. Physical (streaming) replication cannot diverge because it replays WAL byte-for-byte; divergence is impossible unless there is a hardware error affecting the WAL byte stream.

The operational impact is that applications routing reads to a diverged replica may make incorrect business decisions (show wrong inventory counts, incorrect order history, stale financial balances) without any technical signal of a problem. The divergence may persist for the lifetime of the replica before being discovered in an audit or when a user explicitly compares replica and primary data.

Characteristics

Propagationlinear
Time to detectDays to weeks without proactive consistency checking. Immediately detectable when replication stops (MySQL with strict mode, PostgreSQL logical replication). pt-table-checksum can detect divergence within 1–4 hours of a scheduled run. Real-time detection requires application-level data reconciliation queries comparing primary and replica values for sampled records.
Blast radiusAll reads served from the diverged replica return potentially incorrect data. The divergence affects every table that had replication events applied incorrectly after the divergence point. In a read-heavy system where 90% of reads go to replicas, the majority of user-facing data is sourced from incorrect state. Downstream systems populated from replica data (search indexes, analytics pipelines, caches) also contain the incorrect values, extending the blast radius beyond the immediate read path.

Triggers

  • ·Manual data modification executed directly on the replica (not through the primary replication path)
  • ·MySQL slave_exec_mode=IDEMPOTENT configured, silently skipping rows that fail to apply
  • ·Software bug in the MySQL or PostgreSQL replication implementation misapplying a specific event type
  • ·Point-in-time restore applied to a replica without resetting replication to the correct GTID position
  • ·Replication filter configured on the replica that silently drops some events (replicate-ignore-table, binlog-ignore-db)

Detection Signals

replication lagalertlog errors

Mitigation Strategies

Regular consistency checks with pt-table-checksumcomplexity: medium

Run Percona Toolkit's pt-table-checksum weekly (or daily for critical tables) against the primary, which checksums all tables and compares against each replica. Diverged rows are reported by table and approximate row count. Use pt-table-sync to repair diverged rows on the replica. Configure an alert to fire if pt-table-checksum reports diverged chunks on any table. This detects silent divergence within the check interval.

Prohibit direct writes to replicas via database user permissionspreventscomplexity: low

Create a read-only database user for the replica and configure the application to use this user for replica reads. Explicitly deny WRITE and UPDATE permissions on the replica. For operator access, add a policy requiring that all data fixes be applied to the primary and replicated, never directly on the replica. This eliminates the most common cause of divergence (manual modifications) through access control rather than policy.

Switch to PostgreSQL physical streaming replicationpreventscomplexity: high

Replace MySQL row-based replication or PostgreSQL logical replication with PostgreSQL physical streaming replication (WAL shipping). Physical replication cannot diverge because it replays WAL records byte-for-byte. Silent divergence is structurally impossible. The trade-off: physical replicas cannot be used for databases other than the primary's exact version and schema; logical replication allows partial table subscriptions and cross-version replication. For most standard read-scaling use cases, physical replication is the safer choice.

Recovery Steps

  1. 1.Stop routing reads to the suspected diverged replica immediately
  2. 2.Run pt-table-checksum (MySQL) or a manual checksum query (SELECT md5(array_agg(id ORDER BY id)::text) from critical tables) comparing primary vs replica
  3. 3.Identify which tables and approximately how many rows are diverged
  4. 4.For MySQL: use pt-table-sync to repair diverged rows on the replica
  5. 5.For PostgreSQL logical replication: identify the point of divergence from pg_replication_slots, drop and re-create the subscription, and rebuild from a fresh base
  6. 6.After repair, run a second consistency check to confirm divergence is resolved before re-enabling reads

Estimated recovery time: 2–24 hours for repair depending on the volume of diverged rows and table sizes. pt-table-sync can repair at 10,000–50,000 rows/minute. A full replica rebuild (for severe divergence) may require 1–8 hours depending on database size.

Affected Systems

Patterns

read replicacqrswrite ahead log cdc

Technologies

mysqlpostgresql

Basis

MySQL IDEMPOTENT mode silent row-skip behavior is documented in MySQL documentation; pt-table-checksum divergence detection is an established production tool; manual modification as a common divergence cause is a well-known operational hazard; PostgreSQL WAL-level replication divergence immunity is a documented architectural property