Fault Tolerance vs High Availability
A payment service keeps processing requests with zero dropped transactions while one of its three nodes crashes mid-request — that's fault tolerance. A different payment service goes fully offline for four seconds during a failover, then resumes serving traffic — that's high availability, and it is a real, useful property, even though a request landed in that four-second gap and failed. Teams use these two terms interchangeably and then get surprised when a "highly available" system still drops requests during failover.
This guide follows one payment service through both properties. We will define what each one actually promises, build the mechanisms that provide each, show where a system can have one without the other, trace an outage where a genuinely available system still violated an implicit fault-tolerance assumption, and decide which one a given operation actually needs.
Two different promises, easy to conflate
Fault tolerance means the system continues operating correctly through a component failure, with no visible interruption to in-flight work — the failure is masked before it reaches the caller. High availability means the system, as a whole, is reachable and serving requests for a very high percentage of time — but it does not promise that every individual request survives every individual failure. A system can fail over in under a second, count as "available" for the month at 99.99%, and still have dropped every request that was in flight during that second.
The difference is what happens to the specific request or transaction that was active at the moment of failure. Fault tolerance masks that failure from the caller — the request completes as if nothing happened, usually because redundant, synchronized components picked up the exact in-progress work. High availability just means the next request, and the one after that, get served quickly — the system as a measured whole stays up, even if a handful of individual requests did not survive the transition.
Both are legitimate, valuable properties. The problem is choosing a mechanism that provides one while a stakeholder assumes it provides the other — a payment system built for high availability but assumed to be fault-tolerant will surprise someone the first time a failover drops an in-flight charge. This is a sharper, request-level version of the distinction in availability vs reliability vs durability: availability there asks whether the service can be used now, and high availability here is that same question measured over time.
Quick reference
- Fault tolerance: the specific in-flight request survives the failure, masked from the caller.
- High availability: the system as a whole stays reachable and serving, most of the time.
- A highly available system can still drop individual in-flight requests during failover.
- The distinction is about what happens to the request that was active during the failure.
- Mismatched assumptions ('available' architecture, 'fault-tolerant' expectations) cause real incidents.
Remember this
High availability promises the system comes back fast; fault tolerance promises the request that was already running never noticed the failure at all — those are not the same guarantee.
Fault tolerance mechanisms: mask the failure before the caller sees it
Fault tolerance requires redundant components that are actively, synchronously participating in the same unit of work — not just standing by to take over afterward. State machine replication (via Raft or Paxos) has every replica apply the same operations in the same order, so any single replica's crash mid-operation loses nothing: another replica already has the same state and can answer the next request as if nothing happened. Synchronous replication commits a write to multiple nodes before acknowledging it, so a single node's crash immediately after commit does not lose the write.
Idempotent retries provide a weaker, cheaper form of fault tolerance at the client/caller boundary: if the caller can safely retry the exact same operation and get the exact same result, a mid-flight node crash is masked by the retry succeeding against a different, healthy replica — the caller never has to know a failure happened. This only works if the operation is genuinely safe to repeat, which usually means an idempotency key tied to the specific attempt.
All of these mechanisms share a cost: they require doing the work more than once, synchronously, before answering the caller — which is real latency, paid on every request, not just during a failure.
Quick reference
- State machine replication: every replica applies the same ops in the same order.
- Synchronous replication: commit to multiple nodes before acknowledging the caller.
- Idempotent retries: cheap fault tolerance at the boundary, if repetition is genuinely safe.
- All fault-tolerance mechanisms pay a latency cost on every request, not just during failure.
- The goal is that the caller's specific in-flight request completes, not just that service resumes.
Remember this
Fault tolerance is bought with synchronous redundancy or safe retries — the in-flight request has to be reproducible or already replicated before the failure happens, not after.
High availability mechanisms: come back fast, system-wide
High availability is built from health checks, load balancing across replicas, and automated failover — mechanisms that detect a failed component and route around it quickly, so the system as a whole resumes serving new requests without a human paging in. Health checks (liveness and readiness) let a load balancer stop routing to an unhealthy instance within seconds; multi-AZ or multi-region deployment ensures the failure of one physical location doesn't take the whole system down with it.
Asynchronous replication with automated failover is the most common HA pattern for stateful systems: a primary serves writes, replicas lag slightly behind, and if the primary fails, one replica is promoted to primary within a bounded window. This is fast and keeps the system available — but any write acknowledged by the old primary and not yet replicated to the promoted replica is gone. That's availability without fault tolerance for that specific write: the service survived; that write did not.
Measuring high availability means tracking uptime/reachability (often as "nines") over a window, and it is a legitimate, widely useful target — most systems do not need every write to be fault-tolerant, only the system as a whole to keep serving.
Quick reference
- Health checks + load balancing route around a failed instance within seconds.
- Multi-AZ/region deployment protects against one location's failure taking down everything.
- Async replication + automated failover is fast HA — not automatically fault tolerance.
- A promoted replica can be missing the newest acknowledged writes from the old primary.
- HA is measured as uptime/reachability over a window — a legitimate target on its own.
Remember this
High availability mechanisms optimize for the system resuming quickly — they do not, by themselves, guarantee the specific request or write active at failure time survived.
Where they overlap, and where they diverge sharply
A system can have both: synchronous quorum replication (fault tolerance for each write) combined with multi-AZ deployment and automated leader election (high availability for the service as a whole) — this is exactly what systems like properly configured Raft-based stores provide, at the cost of the latency every fault-tolerant write pays. A system can have HA without fault tolerance — the common async-replication-plus-failover pattern above — trading some in-flight data loss for much lower everyday write latency.
The less obvious case is a system with fault tolerance but poor availability: a strict-quorum store that refuses to accept writes at all when it cannot reach a majority is fault-tolerant for every write it does accept, but during a network partition it may become fully unavailable rather than serve a possibly-inconsistent answer — correct, but not "available" during that window. This is the same trade-off CAP theorem describes: consistency-preserving fault tolerance and availability compete directly during a partition.
Most systems don't need uniform treatment — apply fault tolerance to the specific operations where losing an in-flight one is unacceptable (payment capture, order placement), and accept HA-only, faster mechanisms everywhere else (session data, view counts, most reads).
Quick reference
- Both together: synchronous quorum replication + multi-AZ automated failover.
- HA without fault tolerance: async replication + fast failover, some in-flight loss accepted.
- Fault tolerance without full availability: a strict quorum store refusing writes during a partition.
- This mirrors the CAP trade-off directly during a network partition.
- Apply fault tolerance selectively to operations where losing in-flight work is unacceptable.
Remember this
The two properties can be bought together, but each unit costs something different — pay for fault tolerance only on the specific operations where an in-flight loss is genuinely unacceptable.
Failure story: 99.99% available, and the charge still vanished
Trigger. The payment service's primary database crashes mid-transaction while processing a card charge. Automated failover promotes the most caught-up replica within 2.8 seconds — well inside the team's availability SLO. Symptom: the payment service resumes serving new requests almost immediately (availability metrics look fine), but the specific charge that was in flight at the moment of the crash is gone — no record of it exists on the promoted replica, and the customer's card was never actually charged, though the client had already shown a "processing" spinner.
Root mechanism. The database used asynchronous replication with a replication lag of roughly 400ms under normal load. The crash happened after the primary acknowledged the write to the application but before that write had replicated to any standby. The promoted replica was, correctly, the most caught-up one available — it simply never received this specific write. The system's availability recovered in under 3 seconds; that specific transaction did not recover at all, because nothing about the HA mechanism was designed to preserve it.
Evidence. The application log shows a write acknowledged by the primary at T; the promoted replica's write-ahead log shows its last received entry at T - 380ms; the charge is present in neither the new primary nor the payment provider's own confirmed-charge log (the provider call itself failed when the primary crashed mid-flow). Recovery: reconcile against the payment provider's own transaction log, and only re-attempt charges genuinely absent everywhere, using an idempotency key. Prevention: for payment capture specifically, upgrade to synchronous (quorum) replication or an idempotent retry protocol that survives node failure — accepting the latency cost only on this specific, loss-intolerant operation, not the entire service.
Quick reference
- Trigger: primary crashes mid-transaction, right after acking, before replication completes.
- Symptom: service resumes in seconds (HA intact); the specific in-flight charge is gone entirely.
- Mechanism: async replication lag meant the promoted replica never had this write.
- Evidence: gap between the ack timestamp and the promoted replica's last replicated entry.
- Prevention: synchronous replication or idempotent retry for the specific loss-intolerant operation.
Remember this
A 99.99% availability number describes the service, not any individual transaction — a fast failover can hit its SLO perfectly while still losing the exact request everyone assumed 'available' meant safe.
Choosing per operation, not per system
Ask, for each operation: if this specific request is in flight when a node crashes, is losing it acceptable? If no — payment capture, order placement, anything with an external, hard-to-reverse side effect — build fault tolerance for that operation specifically: synchronous replication, quorum writes, or an idempotency protocol the caller actually uses on retry.
If yes — most reads, session refreshes, analytics events, anything cheap to lose or safely re-derived — high availability alone is the right, cheaper choice: async replication, fast automated failover, and a good uptime SLO. Forcing fault-tolerant guarantees onto operations that don't need them pays synchronous-replication latency for no real benefit.
State the chosen guarantee explicitly per operation in design docs and SLAs — "available" and "fault-tolerant" are not interchangeable words, and the gap between them is exactly where incidents like the one above happen.
Quick reference
- Ask per operation: is losing this specific in-flight request acceptable?
- No: build fault tolerance — synchronous replication, quorum, or a real idempotency protocol.
- Yes: high availability alone is faster and cheaper — don't over-build.
- Most systems need fault tolerance on a small set of operations, not uniformly everywhere.
- Document the guarantee explicitly per operation — 'available' is not a synonym for 'fault-tolerant'.
Remember this
Decide fault tolerance vs high availability per operation from the cost of losing its specific in-flight instance — not as one blanket architecture decision for the whole system.
Key takeaway
High availability promises the system keeps serving; fault tolerance promises the specific thing that was happening when a component failed survives that failure. A system can have either without the other, and the mismatch between what an architecture actually provides and what a stakeholder assumes it provides is where real incidents happen — a payment service can be 99.99% available and still lose an individual charge to a fast, well-executed failover. Choose the guarantee per operation from what losing that operation's in-flight instance actually costs.
Practice (30 minutes): pick one write operation in a system you know. Find its replication mode (sync or async) and its failover time. Write down, honestly: if the primary crashed one millisecond after acknowledging this specific write, would that write survive? If you don't know, that's the exact gap this article's failure story traced — go find out before an incident does it for you.
Related Articles
Explore this topic