Skip to content
SeeItFlow
Deep dive · 6 chapters · ~20 min read

etcd Engineering Insights

The engineering decisions behind etcd — why Raft was chosen over Paxos, how MVCC works under the hood, when etcd is the wrong choice, and how etcd typically fails in production.

Mangesh MandlikLast updated:
01 · Consensus

Raft: Why and How

Raft is the consensus algorithm behind etcd's strong consistency guarantee. Understanding Raft means understanding why etcd behaves the way it does under failures.

Swipe to pan
etcd-1DOWN ✗etcd-2CANDIDATEetcd-3FOLLOWERRequestVote(term=2)VoteGrantedetcd-2LEADER ★Majority wins: 2/3 votes → new leader elected for term 2
Raft leader election: failed node, candidate starts campaign, majority votes, new leader elected.

Raft was designed around understandability as an explicit design goal, in contrast with Paxos, which is widely regarded as difficult to implement correctly even though it is formally sound. Raft divides consensus into three independent subproblems: leader election (picking one node to coordinate), log replication (the leader distributing entries to followers), and safety (ensuring the elected leader has all committed entries). Each subproblem has clear invariants that are easy to reason about and test. etcd was created at CoreOS and has been built on Raft since the beginning, so that understandability is paid back directly in a codebase that many contributors and operators have to reason about under failure.

Log matching safety

Raft's safety invariant: if two entries in different nodes' logs have the same index and term, those entries are identical, as are all entries before them. This (the Log Matching property) is guaranteed by the AppendEntries consistency check: a follower accepts new entries only if its log contains an entry matching the leader's preceding index and term. A separate rule, the election constraint, ensures the elected leader always has all committed entries: a candidate can only win if its log is at least as up-to-date as any node that votes for it, meaning its last log entry has either a higher term, or the same term and an index at least as large as the voter's last entry.

Heartbeat mechanism

The leader sends empty AppendEntries RPCs (heartbeats) to followers at a configured heartbeat interval — etcd's default is 100ms. If a follower doesn't receive a heartbeat within the election timeout (etcd's default is 1000ms), it starts a new election. The election timeout must be meaningfully larger than the heartbeat interval, scaled to the real round-trip time between members, to avoid spurious elections.

Pre-vote protocol

etcd implements Raft's Pre-Vote extension (available in current etcd releases; check your version's defaults): before starting a real election, which increments the term and forces other nodes to step down, a candidate first runs a pre-vote to check whether it could actually win. This prevents a node that was network-partitioned and ran elections in isolation from disrupting the healthy cluster with an inflated term purely because it rejoined.

02 · Comparisons

etcd vs Redis vs ZooKeeper vs Consul

The hardest architectural decision isn't whether to use etcd — it's knowing when NOT to use it and what to use instead.

etcd vs Redis

Redis is optimized for throughput and low latency; etcd is optimized for coordination correctness. Redis Cluster distributes keys across shards and each shard is independently consistent, but the cluster as a whole doesn't provide the same linearizable, single-log view across all keys that etcd does — a meaningfully different consistency model, not simply 'less consistent.' For distributed locking, Redis's Redlock algorithm has been publicly debated for its behavior under clock skew and long GC/process pauses, while etcd's CAS transactions are backed by Raft's linearizable log. That difference matters more for locks protecting correctness-critical external actions than for best-effort use cases like rate limiting or cache-stampede protection, where Redis is a reasonable fit.

etcd vs ZooKeeper

ZooKeeper is the older distributed coordination service (used by Hadoop and, historically, Kafka). It uses ZAB (ZooKeeper Atomic Broadcast) for consensus, provides a hierarchical namespace (znodes), and exposes ephemeral nodes and watches. etcd covers similar conceptual ground — leases are roughly analogous to ephemeral nodes, Watch to ZK watches, CAS to ZK version-based conditionals — with a simpler operational footprint: a single Go binary, a gRPC API, and no JVM dependency. ZooKeeper remains in active production use in many existing systems; etcd's main practical advantage for new systems is operational simplicity and being Kubernetes' native backing store, not a categorical correctness advantage.

etcd vs Consul

Consul bundles service discovery, health checking, a KV store, and a service mesh in one product. etcd provides only the strongly consistent KV store and leaves discovery/health-checking/mesh features to other tools. Consul's Raft-backed KV layer is also strongly consistent, but Consul's broader feature set introduces more moving parts. In Kubernetes environments etcd is used because it's the built-in backing store behind kube-apiserver; outside Kubernetes, Consul can be a better fit when integrated health checking and DNS-based discovery outweigh the benefit of a smaller, single-purpose store.

03 · Internals

MVCC and BoltDB Internals

etcd's storage engine is a custom MVCC layer on top of BoltDB. Understanding it explains both its performance characteristics and its operational limits.

bbolt is an embedded key-value store that organizes data in a B-tree structure on disk. It provides ACID transactions with MVCC semantics — readers operate against a consistent snapshot without blocking writers. etcd uses bbolt as its persistence layer and implements its own MVCC index on top: an in-memory index that maps each key to its revisions, while the key-value data itself is stored in bbolt keyed by revision; because revisions are indexed too, ranging over revisions with watchers is efficient.

The practical consequence: reads are fast B-tree lookups, and sequential scans of a prefix are efficient range scans. As the backend database grows, operation latency can be affected, which is why etcd's operational guidance treats keeping the database reasonably small as good practice rather than storing large volumes of data in it. Compaction logically removes old revisions; defragmentation is the separate step that returns the resulting free space to the filesystem, since bbolt doesn't reclaim freed pages automatically.

04 · Scalability

Hot Keys and Write Limits

etcd handles coordination workloads, not high-throughput business data. Its write path has deliberate bottlenecks that reflect its design goals.

Every write goes through the Raft leader and requires a quorum round-trip plus an fsync before it commits. Actual sustained write throughput depends heavily on hardware, request size, and network topology, so there's no single number that applies universally — but the design intentionally trades raw throughput for durability and linearizability. The useful mental model isn't a specific writes/sec ceiling; it's that etcd optimizes for coordination correctness and predictable consensus behavior rather than high-volume application-data throughput.

Hot key antipattern

Updating the same key at high frequency (for example, using etcd as a counter) works against its consistency model and puts sustained pressure on the Raft log for no architectural benefit. Use a store built for high-frequency counters (Redis, for instance) and reserve etcd for state that changes rarely but must be read consistently by many clients.

05 · Failure Scenarios

Representative Failure Scenarios and Lessons

These are representative failure scenarios — illustrative patterns worth recognizing, not claims about specific documented incidents. Each one teaches something specific about how etcd fails in practice.

Noisy-neighbor disk contention

A common pattern: etcd shares a disk with another I/O-heavy process (a database doing bulk writes, a log shipper, a search index doing bulk indexing). The other workload saturates the disk with random writes, WAL fsync latency spikes, and etcd starts missing heartbeats and running unnecessary leader elections. Lesson: disk contention from co-located workloads is one of the most common real causes of etcd instability, which is why isolating etcd's disk matters more than any specific hardware tier.

Unmonitored backend growth

A cluster runs without etcd_mvcc_db_total_size_in_bytes monitoring or an auto-compaction policy. MVCC history accumulates silently until the backend approaches its quota, apply latency rises as the backend grows, and the eventual emergency compaction/defragmentation happens under pressure instead of as routine maintenance. Lesson: monitor backend size and enable auto-compaction proactively — defragmentation under pressure, during an incident, is a worse experience than defragmentation on a schedule.

Partition without Pre-Vote

Before Pre-Vote existed, a member isolated by a network partition would keep incrementing its term while unable to reach a quorum. When the partition healed, that inflated term could force the healthy leader to step down, triggering a disruptive cascade of elections even though the cluster had been stable. Supported etcd versions implement Pre-Vote specifically to prevent an isolated node's elections from disrupting the healthy cluster on rejoin.

06 · Data Model

The etcd Data Model in Depth

etcd's data model is richer than a simple string→string map. Understanding revisions, leases, and the key space hierarchy is essential for building correct applications on top of it.

The cluster revision is a monotonically increasing counter over the key space. It starts at 1 when the store is created, and each atomic mutating operation on the key space (a put, a delete, or a transaction that writes) creates a new revision — reads, watch progress notifications, and operations that don't change the key space do not advance it. When you read a key, you get its value along with create_revision, mod_revision, and version, which together describe the key's history.

The key space is a flat byte-string namespace with no native directory structure — a hierarchy like /services/payment/instance-1 is purely a naming convention the application imposes. The only structure etcd itself understands is lexicographic key ordering, which is what makes prefix range scans efficient. This is simpler and more predictable than ZooKeeper's hierarchical znodes, but it means applications have to think deliberately about key naming.

Key takeaways
  1. 01Raft was chosen for understandability: election, log replication and safety are separable, which makes a consensus core easier to implement and test than Paxos.
  2. 02Against ZooKeeper, Consul and Redis, etcd's niche is linearizable coordination data with revision-based, resumable Watch — not high-volume storage or caching.
  3. 03MVCC keeps history until compaction, so backend size tracks write churn, not just live key count; watch it against the quota.
  4. 04Using etcd for high-frequency writes (a hot key, a counter) puts sustained pressure on the Raft log, and disk contention that slows WAL fsync is the classic way etcd destabilises.
  5. 05Pre-Vote prevents an isolated member from inflating its term and disrupting a healthy leader when the partition heals.