Interview Prep Zoneby CuriouserLabs

Question 2 of 5 · senior level

Synchronous vs asynchronous replication — what exactly do you lose on failover?

🎤 Say this first

With asynchronous replication the leader acknowledges the client as soon as it has the write; followers catch up later. Writes are fast and the leader never blocks on a slow replica — but if the leader dies with unreplicated writes, those writes are gone, and they were already acknowledged to the user. That gap is your RPO (recovery point objective). With synchronous replication the leader waits for the replica to confirm before acknowledging, so RPO is zero — but write latency now includes the replica's round-trip, and if the replica stalls, writes stall with it. That's why nobody runs fully synchronous replication to all replicas: the practical answer is semi-synchronous (wait for one, or for a majority) which bounds data loss while tolerating any single slow node.

The full picture

ModeAck afterRPOFailure behaviour
Asyncleader's own commitseconds of writesfast; silent data loss on failover
Semi-sync (1 replica)leader + 1 follower≈0 for single-node lossone slow replica → fallback or stall
Quorum (majority)leader + majority0 for minority lossthe consensus default (Kafka acks=all, w:majority)
Full sync (all)every replica0availability of the weakest node — effectively unusable
Durability is a per-write decision — and defaults usually lie
// Kafka: acks=1 acknowledges on the leader only. A leader crash loses it.
props.put(ProducerConfig.ACKS_CONFIG, "all");            // wait for the in-sync replicas
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // no duplicates on retry
// ... and on the broker, min.insync.replicas=2, otherwise "all" can mean "just the leader"
// when the ISR has shrunk to one. Both settings are needed; either alone is a false sense
// of safety.

// MongoDB: the write concern travels with the operation.
collection.withWriteConcern(WriteConcern.MAJORITY)   // durable across an election
          .insertOne(order);

// Postgres: synchronous_commit + synchronous_standby_names decide the same trade-off.
  • Kafka's `acks=all` + `min.insync.replicas` pairing is the classic gotcha: acks=all means 'all in-sync replicas', and if replicas have fallen out of the ISR that set can be just the leader. Without min.insync.replicas=2 you can lose acknowledged messages while believing you configured maximum durability. This is a genuinely common production incident.
  • Chained/cascading failure is the argument against full sync: with every replica required, your write availability is the product of each replica's availability — more replicas make you less available. Majority quorums invert that: more replicas make you more tolerant. Stating this inversion clearly is a strong signal.
  • RPO and RTO are the vocabulary to use with stakeholders: RPO = how much data you may lose (set by replication mode), RTO = how long recovery takes (set by failover detection + election + cache warm-up). Product owners can reason about those numbers; 'async replication' means nothing to them.
  • Failover isn't free even at RPO 0: detection timeout, election, client reconnection and a cold cache typically make the observable outage much longer than the election itself. Systems that look instantly failed-over in a demo take 30+ seconds under real load.
  • The dangerous middle ground: async replication plus reads-from-replica plus automatic failover promotes a lagging replica and silently rolls back acknowledged writes. If you run async, either accept a manual failover step or use a store that refuses to promote a replica behind by more than a bounded amount.

🔄 Likely follow-up questions

  • Why does requiring all replicas to ack make a system less available, not more?
  • What exactly does acks=all guarantee when the ISR has shrunk to one?
  • How would you measure your real RPO rather than assuming it?