← Back
Home › Systems › Cloud platform and compute

coordination-store

Provides a small, strongly consistent key-value store that other services use to agree on leaders, membership, configuration and locks.

etcd, ZooKeeper

Open the coordination-store pagecoordination-store Fencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›Leasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Linearizable readsdistributed: ReadIndex and leader-lease reads without writing to the logMulti-Paxosdistributed: Paxos run once per log slot with a stable leader that skips phase 1; the foundation of Chubby, Spanner and much production consensus.›Raftdistributed: Leader-based consensus on a replicated log; one leader per term, majority commit; the default for a strongly consistent replicated state machine.›State-machine snapshotsdistributed: Periodically save the state a log has produced at index i so the log prefix can be deleted; recovery and catch-up become load snapshot plus replay suffix.›Watch notificationsdistributed: A store numbers every write; clients list at revision R, then watch from R+1 and receive an ordered change stream with no gap, no duplicate, and a resume point.›CRC32C checksumshardware-os: The Castagnoli CRC-32 with dedicated instructions on x86 (SSE4.2) and ARMv8. It is the standard fast integrity check for storage blocks, logs and network frames.›fsync, O_DSYNC and O_DIRECThardware-os: What fsync, fdatasync, O_DSYNC, O_DIRECT and F_FULLFSYNC really guarantee on Linux and macOS, and the exact call sequences for crash-safe files.›Grpc protocolnetworking: GRPC framing over HTTP/2: unary and streaming RPCs, deadlines, statusB+ treestorage: Balanced high-fanout search tree with all values in linked leaves; the default on-disk index for point lookups and range scans.›Group commitstorage: Let concurrent committers share one log write and fsync; throughput grows with concurrency while each commit still waits at most about two fsyncs.›MVCCstorage: Keep timestamped versions of each row so readers see a consistent snapshot without blocking writers; the default concurrency control in modern databases.›Optimistic concurrency controlstorage: Run transactions without locks, record what they read, and validate at commit that nothing changed; fastest when conflicts are rare.›Write-ahead logstorage: Append each change to a checksummed sequential log and fsync it before touching data pages; the basis of crash durability in nearly every database.›Exponential backoff with jittersystems: Retry after delays that grow geometrically and are randomized, so failed clients spread out instead of hammering a recovering service in synchronized waves.›Copy-on-write forkhardware-os: fork() gives the child the parent's whole address space by sharing pages read-only and copying one only when someone writes it; vfork and posix_spawn skip even that.›Copy on write snapshotsstorage: Point-in-time snapshots by sharing pages/extents and copying only on writeMessage sequence numbersdistributed: A per-stream counter on every message lets the receiver detect loss, duplicates and reordering, ack cheaply and resume after reconnect with "send from N".›SIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›Futex-based mutexconcurrency: A mutex that is one atomic word in user space and enters the kernel (futex wait/wake) only under contention; how std and glibc locks work.›PRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›

How it is built

Architecture overview

A coordination store is a small, strongly consistent key-value database that a fleet of other services uses to agree on facts: who is the leader, which nodes are alive, what the current configuration is, who holds a lock. It holds little data (metadata, measured in megabytes to a few gigabytes), but every read and write has to be right, because the services above it make decisions from the answer. The one idea that makes it work is a replicated log under a state machine: a small odd number of servers (three or five) run a consensus protocol (Raft in etcd and Consul, Zab in ZooKeeper, Paxos in Chubby) that puts every write into one agreed order, and each server applies that order to the same in-memory and on-disk key space. On top sits what makes it a coordination primitive rather than just a database: versioned keys, a watch that streams changes, and leases (TTL-bound ownership) so that dead clients lose their locks and ephemeral registrations automatically.

Clients do not use the store as a general database. They use it to build locks, leader election, barriers, service registries and configuration distribution, and Kubernetes uses it as the backing store for all cluster state (source: Kubernetes docs, etcd operations).

Parts:

 clients ──gRPC──► any member ──forward──► LEADER ──append──► WAL (fsync, batched)
   ▲  ▲                                      │  replicate entries
   │  └── watch stream (revision R+1…) ◄─┐   ├──► follower ─► WAL ─► ack
   │                                      │   └──► follower ─► WAL ─► ack
   └── reply after commit ◄── apply ◄─ majority acked
                               │
                               ▼
              state machine: key space + revisions (B+ tree) ─► snapshot + compaction
              lease table: TTL timers, expiry deletes attached keys

How the parts fit together

Consensus

Every write is a command proposed to the leader, appended to its log, copied to followers and committed once a majority has it on disk. Raft fixes the roles (leader, follower, candidate), terms, and the log-matching rule in a form built to be implementable; Multi-Paxos is the older equivalent that Chubby and Spanner are built on, with a stable leader that skips the first phase. Viewstamped Replication is a third relative (no page yet). ZooKeeper uses its own atomic broadcast protocol, Zab, with the same leader-and-majority shape (source: Hunt et al. 2010).

The cluster size is the main design choice: with n = 2f + 1 members the group survives f failures, so 3 members tolerate one and 5 tolerate two; an even size adds a failure without adding tolerance. Bigger groups make every commit wait for more acknowledgements without adding capacity: replication buys availability, not throughput. Election timeouts are randomised (Exponential backoff with jitter is the same idea) so two followers do not time out together and split the vote.

Log and storage

The commit latency is the disk's fsync latency, plus a round trip to the nearest follower. Entries are appended to a Write-ahead log and made durable with an fsync before the member acknowledges (fsync and direct I/O covers what the call really guarantees). Because that fsync dominates, concurrent proposals are batched: one fsync covers many entries (Group commit). Each log record carries a checksum (CRC32C checksums) so a torn tail after a crash is detected and cut rather than replayed.

The applied state lives in a key space. etcd keeps it in an embedded B+-tree database, bbolt, plus an in-memory index from key to revisions. A B+ tree is the right structure for a read-heavy, ordered, range-scanned key space of modest size; a write-heavy store would prefer an LSM tree, and the alternative is explicit in the graph. ZooKeeper instead keeps an in-memory image of the state, with transaction logs and snapshots on disk, which is why it values small data and fast reads (source: ZooKeeper docs, overview).

Versioned key space

MVCC is what lets a coordination store offer more than "latest value". Every write gets a global, monotonically increasing revision, and a key remembers the revisions at which it changed. That gives you three things: a consistent snapshot at one revision for a multi-key read, compare-and-swap on a key's version (Optimistic concurrency control: read, then write "only if the version is still N"), and a point to resume a watch from. etcd exposes this as transactions with compare, then, else branches (source: etcd docs, API guarantees). The cost is that history grows until it is compacted, and etcd's database does not shrink until it is defragmented (source: etcd docs, maintenance).

Snapshots and compaction

An unbounded log cannot be replayed on restart or sent to a lagging follower. State machine snapshots save the key space as of log index i so the log prefix can be deleted; a new or far-behind follower loads the snapshot, then replays the suffix. The snapshot must be taken without freezing writes: either copy-on-write pages in the storage engine (copy-on-write-snapshots, no page yet) or a process fork that shares memory pages with the parent until they change (Copy-on-write fork), the way some in-memory stores snapshot. Log compaction and MVCC history compaction are two separate things and both must run.

Read path

By default a read must be linearizable: it must see every write that finished before it started. The naive way sends reads through the log like writes, which is correct but slow. The faster way, linearizable-reads (no page yet), has the leader confirm it is still the leader (a quorum round trip) or hold a time-bounded leader lease, then answer from its applied state, with no log write. etcd also offers a "serializable" read that any member answers from local state, fast but possibly stale (source: etcd docs, API guarantees). ZooKeeper made the opposite default: reads are served by the member the client is connected to, which may lag, and a sync call forces catch-up (source: ZooKeeper docs, programmer's guide). Choose per call: leader election and locking need the linearizable read; a dashboard does not.

Watch and leases

A watch is how clients avoid polling. The client reads at revision R, then asks to watch from R + 1, and receives an ordered stream of changes with no gap and no duplicate, including a resume point after a reconnect (Message sequence numbers is the general mechanism). etcd guarantees that a watch never drops events within the available history window, and that all changes from one operation arrive in one response (source: etcd docs, API guarantees). ZooKeeper's watches are one-time triggers and the client must re-read and re-set the watch, so it can miss intermediate changes (source: ZooKeeper docs, programmer's guide). The streams run over a multiplexed RPC channel, grpc-protocol (no page yet) in etcd.

A lease is a lock that expires on its own: the client creates a lease with a TTL, attaches keys to it, and refreshes it with keep-alives; if the client dies, the lease expires and the keys vanish. ZooKeeper reaches the same effect with sessions and ephemeral nodes. This is what makes service registration and leader election self-cleaning. Expiry is based on time, so a client that merely pauses (a long garbage-collection stop, a frozen VM) can believe it still holds a lock the store has already given away. The cure is a fencing token: each lock grant carries a number (the key's revision works) that the protected resource checks, rejecting any write with an older token than it has seen.

Failure detection

Two kinds of liveness are tracked. Client liveness is lease or session expiry: simple, fixed timeout. Member liveness inside the consensus group is heartbeats from the leader, and the missed-heartbeat timeout triggers an election. Fixed timeouts are brittle under variable latency; an adaptive detector such as the Phi accrual failure detector scales suspicion to the observed heartbeat distribution, and gossip (SWIM gossip) spreads membership without a central list, which is how Consul tracks its wider fleet beside its Raft servers. Client-side health checking with hysteresis keeps a registry from flapping.

Data and control flow

A write (for example, put key value):

  1. The client sends the request to any member; a follower forwards it to the leader.
  2. The leader appends it to its log, batches it with other pending entries, and sends the batch to followers in parallel.
  3. Each member writes the entries to its WAL and fsyncs; this is where the time goes. Followers acknowledge.
  4. When a majority has the entry, the leader marks it committed and applies it: new revision, update the key space, trigger the watchers of that key.
  5. The leader replies to the client; followers apply the same entry when they learn it is committed.

Leader election for an application (lock on a key):

  1. Each candidate creates a lease and writes a key such as /election/<lease-id> attached to it, in a transaction that records the key's creation revision.
  2. The candidate with the lowest creation revision is the leader. Everyone else watches the key just before theirs (the predecessor only), not the whole prefix, to avoid a thundering herd on every release.
  3. The leader keeps its lease alive. If it dies or is partitioned, the lease expires, its key is deleted, and the next candidate's watch fires.
  4. The new leader uses its revision as a fencing token on every write to shared resources.

Watch after a disconnect:

  1. The client remembers the revision of the last event it processed.
  2. It reconnects (to any member, with backoff and jitter so clients do not all come back at the same moment) and asks to watch from that revision plus one.
  3. If the revision is still in the history, the member replays the missed events in order. If it has been compacted, the client gets an error and must list the whole prefix again, then start a new watch.

Scaling

What limits a coordination store, in the order you hit it:

  1. Write latency is fsync plus one round trip to the nearest majority. Levers: fast, low-latency local SSDs for the WAL, members in one region or nearby zones, group commit; never spread a group over distant regions unless you accept that latency on every write.
  2. Write throughput is bound by the leader: one node orders everything. Levers: batch client operations in transactions, reduce write churn (lease keep-alives and frequently updated keys are the usual culprits), and shard by running separate clusters per concern. Adding members does not raise write throughput.
  3. Read throughput can scale: serializable reads from followers, or observer members in ZooKeeper, or caching in clients that watch. The price is staleness, which you control per call.
  4. Data size: the store is designed for small metadata. etcd has a default storage quota of 2 GiB and suggests 8 GiB as a normal maximum, and a default request size limit of 1.5 MiB (source: etcd docs, system limits). Levers: compact and defragment, store pointers instead of blobs, keep large objects elsewhere.
  5. Watchers and connections: each watch costs memory and each event fans out to all watchers. Levers: watch narrow prefixes, share one watch in a client-side cache (the pattern behind informer caches), put a proxy in front.

Failure modes

FailureSymptomCauseFix
Slow diskfrequent leader changes, slow commits, "took too long" warningsfsync latency spikes, shared disks, noisy neighboursdedicated fast storage for the WAL, tune election timeout to the disk and network
Quorum lossall writes fail, reads may stallmajority of members down or partitionedrestore members; as a last resort restore from a snapshot into a new cluster
Database fulletcd raises an alarm and accepts only reads and deleteshistory never compacted, large churnenable auto-compaction, compact, defragment, clear the alarm (source: etcd docs, maintenance)
Split lock holdertwo clients both act as leaderlease expired during a client pause, client did not checkfencing token checked by the resource; client stops work when its keep-alive fails
Watch stormCPU and memory spike on reconnectmany clients re-list after compaction, or all reconnect at oncejittered reconnect, watch from last revision, narrower prefixes
Missed eventsa client's view drifts from the storeone-time watch not re-armed (ZooKeeper), or compaction passed the client's revisionre-list then watch, level-triggered logic that rereads state
Even-sized groupno more failure tolerance than size minus one4 or 6 membersuse 3 or 5
Large valueslatency of other clients risesbig payloads in the key spacekeep values small, store blobs in object storage and a pointer here
Clock skewleases expire early or latelease TTLs depend on clocksbound drift, leader-side timers, conservative TTLs (Leases)

Real implementations

namewhat it isrepolicencenotes
etcdRaft-based key-value store with MVCC, watch, leases, transactionsetcd-io/etcdApache-2.0backing store of Kubernetes; storage is bbolt, the Raft library is separate
etcd raft librarythe Raft implementation embedded in etcd and reused by other databasesetcd-io/raftApache-2.0library only: you supply storage and transport
bboltembedded B+-tree key-value database used by etcdetcd-io/bboltMITsingle-file B+-tree database
Apache ZooKeeperhierarchical namespace of znodes, ephemeral nodes, one-time watches, Zabapache/zookeeperApache-2.0in-memory data tree; long-standing base of Hadoop-era systems
Apache Curatorclient recipes for ZooKeeper (locks, election, barriers)apache/curatorApache-2.0shows the recipes built on the primitives
ConsulRaft servers plus gossip membership, service catalog, key-valuehashicorp/consulBUSL-1.1 from 1.17.0 (LICENSE read)source-available since that version
ChubbyGoogle's lock service: coarse-grained locks and small filespaperproprietary, in-housethe design that ZooKeeper and etcd answer

Choice: pick etcd when you want a linearizable key-value API with revisions, watch-from-revision and leases (typical for Kubernetes-style control planes); ZooKeeper if you already run the Hadoop or Kafka-era stack that expects it; Consul when service discovery with health checks and multi-datacenter features is the main job.

Sources

  • D. Ongaro and J. Ousterhout, In Search of an Understandable Consensus Algorithm (Raft), USENIX ATC 2014. paper
  • P. Hunt et al., ZooKeeper: Wait-free Coordination for Internet-scale Systems, USENIX ATC 2010. paper
  • M. Burrows, The Chubby Lock Service for Loosely-Coupled Distributed Systems, OSDI 2006. page
  • etcd docs: API guarantees (linearizability, watch guarantees), system limits, maintenance (compaction, defragmentation, space quota alarm).
  • Apache ZooKeeper docs: overview, programmer's guide (watches).
  • Kubernetes docs: operating etcd clusters.
  • Licence texts and descriptions read through the GitHub API.

Similar systems

Share the most building blocks with this one.

In-house systems

Named systems of this kind at large companies, with their paper or write-up.