ZooKeeper follower doing a SNAP sync: full snapshot transfer and its blast radius
A follower that fell too far behind the leader does not catch up transaction by transaction. Once its last-seen zxid is older than the leader’s retained transaction log, the leader ships the entire data tree as a snapshot. This is a SNAP sync, the most expensive recovery path a healthy ensemble runs short of a leader election.
The blast radius is not limited to the recovering follower. While the leader serializes and transmits the full tree, its request pipeline competes for CPU and network. Clients writing to the leader see elevated latency for the duration. The recovering follower is meanwhile unavailable for reads: it is loading a snapshot, not serving requests. In a three-node ensemble, a SNAP sync temporarily cuts read capacity by a third and stretches write latency on the only node that handles writes.
SNAP syncs are not bugs. They are the recovery path ZooKeeper uses when DIFF sync (replay the missing transactions) is no longer possible because the leader has already purged those transactions. The operator’s job is not to prevent every SNAP sync, but to make them rare, short, and non-cascading.
What this means
ZAB Phase 2 synchronization has three modes the leader picks between when a follower reconnects:
- DIFF: the leader still holds the missing transactions in its in-memory committedLog or on-disk transaction log. It replays them. Cheap, common, fast.
- TRUNC: the follower has transactions the leader does not (for example from a brief split-brain). The leader tells the follower to roll back. Also cheap.
- SNAP: the leader no longer has what the follower needs. It serializes the entire in-memory DataTree and streams it as a snapshot. Expensive on both ends.
The leader compares the follower’s last processed zxid against its retained transaction log. If the gap exceeds what the log covers, SNAP is the only option.
flowchart TD F[Follower zxid behind retained log] -->|triggers| L[Leader selects SNAP sync] L --> S[Leader serializes full DataTree] S -->|CPU contention| LD[Leader write latency rises for ALL clients] L -->|streams snapshot| FR[Follower receives snapshot] FR --> FL[Follower deserializes into heap] FL -->|during load| NR[Follower serves NO reads] LD --> Q[Outstanding requests grow on leader]
The leader-side cost is twofold: serializing the data tree is CPU work that competes with the single-threaded request pipeline, and transmitting the snapshot is sustained network output that competes with PROPOSE, ACK, and COMMIT traffic. The follower-side cost: it must deserialize the snapshot into heap before it can participate in the ensemble, and during that window it serves no reads.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Extended follower downtime | Maintenance that ran long, follower restart loop, or a node cordoned for hours | Follower uptime versus the leader’s oldest retained transaction log zxid |
autopurge.snapRetainCount too low | Only the minimum snapshots retained; logs roll past the follower’s last zxid quickly | zoo.cfg autopurge settings and on-disk log count |
| Very high write rate | Large zxid delta between last snapshot and present; transaction log advances faster than a recovering follower can catch up | zk_proposal_count rate versus historical baseline |
Oversized transaction logs from high snapCount | Single log files in the hundreds of MB to GB range; sync mode selection may go wrong | snapCount value and log file sizes on disk |
| Network partition isolating the follower | Follower zxid frozen while leader continues advancing; reconnection triggers SNAP | Inter-ensemble network metrics and zk_looking_count |
| Follower disk failure and replacement | Follower restarted with empty dataDir; no choice but SNAP | Follower uptime and dataDir contents |
Quick checks
All safe, read-only operations. On ZooKeeper 3.5+, the 4lw commands (mntr, srvr) require 4lw.commands.whitelist to be set in zoo.cfg.
# Identify the leader and followers
echo mntr | nc localhost 2181 | grep zk_server_state
echo srvr | nc localhost 2181 | grep Mode
# Compare zxids across the ensemble. A large gap is the SNAP sync trigger.
for host in zk1 zk2 zk3; do
echo "$host: $(echo mntr | nc $host 2181 | grep zk_zxid)"
done
# On the leader: see how many followers are fully synced
echo mntr | nc leader 2181 | grep -E 'zk_(followers|synced_followers|pending_syncs)'
# SNAP sync log signature on the leader and the follower
grep -E "Sending snapshot|SNAP" /var/log/zookeeper/zookeeper.log | tail -20
grep -E "Loading snapshot|Snapshotting" /var/log/zookeeper/zookeeper.log | tail -20
# Confirm autopurge and snapshot retention configuration
grep -E "autopurge|snapRetainCount|snapCount|dataLogDir" /etc/zookeeper/zoo.cfg
# Confirm initLimit and syncLimit; SNAP syncs can blow past initLimit
grep -E "^(tickTime|initLimit|syncLimit)" /etc/zookeeper/zoo.cfg
# Watch leader-side disk and network output during the transfer
iostat -x 1
sar -n DEV 1
How to diagnose it
Confirm a SNAP sync is actually in progress. The leader log should show “Sending snapshot” and the follower log should show “Loading snapshot” or “Snapshotting”. If neither appears, you may be looking at slow DIFF sync or a follower stuck in LOOKING.
Confirm the follower is the laggard. Compare
zk_zxidacross all ensemble members. The follower in SNAP sync will have a zxid frozen at an old value while the leader continues advancing.Assess the blast radius on the leader. While the SNAP is in flight, watch
zk_outstanding_requests,zk_avg_updatelatency(orzk_avg_latencyon pre-3.6), andzk_fsynctimeon the leader. Any sustained climb that begins when “Sending snapshot” appears and resolves when the follower rejoins is the SNAP sync degrading leader performance.Confirm the recovering follower is not serving reads. Its
zk_num_alive_connectionsmay still be non-zero because clients reconnect to it, but reads will fail or stall until the snapshot is loaded.Estimate remaining sync time. There is no built-in progress metric. The signal is the follower’s
zk_zxidjumping to the leader’s current value when NEWLEADER is processed. Until that jump, assume the sync is ongoing.Check for cascading SNAP syncs. If
maxConcurrentSnapSyncsallows multiple simultaneous SNAP syncs and several followers restart at once, the leader can be saturated by serialization work alone.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
zk_server_state | Confirms which node is leader and which is the laggard | Laggard cycling between FOLLOWING and LOOKING |
zk_synced_followers (leader only) | Drops below ensemble_size - 1 during the sync | Stays below expected for longer than initLimit * tickTime |
zk_pending_syncs (leader only) | Non-zero means followers are mid-sync | Sustained non-zero, especially growing |
zk_follower_sync_time | Directly measures how long syncs take | Approaching syncLimit * tickTime |
zk_zxid per node | The lag signal | Laggard frozen well behind leader |
zk_outstanding_requests on leader | Pipeline backup from leader-side serialization work | Climbing during the transfer window |
zk_avg_updatelatency / zk_p99_updatelatency | Write latency degradation affecting all clients | Spikes that correlate with “Sending snapshot” log entries |
zk_num_alive_connections on the laggard | Clients still routed to the recovering follower | Connections held while reads stall |
| Host network output on leader | Bandwidth consumed by snapshot transfer | Sustained saturation of the leader-follower link |
zk_snapshot_error_count | Snapshot creation or loading failures | Any increment |
Fixes
During an active SNAP sync
The first lever is traffic shaping on the leader. maxConcurrentSnapSyncs caps how many followers the leader will fully sync at once; additional followers wait. Lowering this value protects a leader with constrained I/O or network during mass-restart scenarios, at the cost of slower ensemble convergence. The companion knob maxConcurrentDiffSyncs does the same for DIFF syncs.
The second lever is client routing. If the leader’s zk_outstanding_requests and write latency are climbing during the transfer, shift read traffic away from the leader and away from the recovering follower. The remaining healthy followers should absorb reads.
Do not restart the leader to “fix” a SNAP sync in progress. You will abort the sync, force a new election, and the new leader will start the snapshot over.
Preventing recurrence
Tune autopurge carefully. autopurge.snapRetainCount defaults to 3 and the minimum is 3. Lower is not permitted, and going higher than needed keeps more logs on disk without operational benefit. Pair it with a sane autopurge.purgeInterval (in hours); leaving it at 0 disables purge, which is the path to disk-full incidents.
Size initLimit for your data tree. initLimit (in ticks) bounds how long followers have to connect and sync to a newly-elected leader. If a SNAP transfer takes longer than initLimit * tickTime, the leader abandons leadership and the ensemble re-elects. With defaults (initLimit=10, tickTime=2000ms), the budget is 20 seconds. A large snapshot with hundreds of thousands of znodes can exceed that on slower disks. Increase initLimit proportionally to snapshot size; do not change tickTime to compensate.
Watch snapCount and transaction log size. The default snapCount of 100,000 triggers snapshots based on transaction count. Two related knobs help when log files balloon: zookeeper.snapSizeLimitInKb triggers snapshots based on log size, and zookeeper.txnLogSizeLimitInKb caps individual log file size. The admin guide explicitly warns that larger transaction logs slow follower sync.
Handling the oversized-transaction-log trap
If you have raised snapCount substantially (for example to 10,000,000) to reduce snapshot frequency, you may have fallen into a known bug where the leader underestimates retained transaction log size and chooses DIFF replay over SNAP even when SNAP would be cheaper. The symptom is sync times jumping from a few seconds to 20+ seconds, with the leader replaying a very large transaction log instead of shipping a smaller snapshot. The workaround is zookeeper.forceSnapshotSync=true.
Version-specific sync hazards
Several recent ZooKeeper versions have SNAP and DIFF sync bugs that affect data integrity. Before treating a SNAP sync as routine, confirm you are not running a vulnerable version.
- 3.9.3 only: A DIFF sync bug introduces a hole in committedLog, leading to data loss. Reportedly fixed in 3.9.4 and 3.10.0.
- 3.9.0 through 3.9.3: Lock contention between snapshotting and the sync operation on follower servers slows SNAP syncs. Reportedly fixed in 3.9.4.
- 3.8.x before 3.8.4, 3.9.x before 3.9.2: A race in DIFF sync where the follower updates its epoch and ACKs NEWLEADER before persisting uncommitted transactions. A crash at that point can lose data.
- 3.5.x before 3.5.10, 3.6.x before 3.7.0: Data inconsistency when the leader crashes after sending SNAP but before sending NEWLEADER . The follower advances lastProcessedZxid without persisting the snapshot.
If you observe zk_digest_mismatches_count incrementing after a sync on any of these versions, treat it as a symptom of the bugs above, not transient corruption.
Prevention
- Keep transaction logs sized for DIFF sync. Use
snapCount,snapSizeLimitInKb, andtxnLogSizeLimitInKbtogether so the leader can almost always satisfy a returning follower with DIFF rather than SNAP. - Size
initLimitfor your worst-case snapshot. Measure actual snapshot load times in pre-prod and setinitLimit * tickTimecomfortably above the p99. - Avoid long follower downtimes during maintenance. Drain and restore followers quickly; the longer one is out, the more likely its zxid falls off the retained log.
- Upgrade off vulnerable versions. Recent point releases carry sync-related data integrity fixes.
- Run with a separate
dataLogDir. Shared txnlog and snapshot disks make SNAP syncs slower on both ends and increase the chance the sync blows pastinitLimit. - Instrument leader-side latency during any planned follower restart. A routine rolling restart that triggers SNAP syncs is the most common way teams discover their
snapRetainCountis too low or their transaction logs are oversized.
How Netdata helps
- Per-second
zk_server_state,zk_synced_followers, andzk_pending_syncslet you see the SNAP sync window with tight time resolution, instead of discovering after the fact that the leader’s write latency degraded for two minutes. zk_follower_sync_timeandzk_zxidper node, collected from every ensemble member and filterable to the leader, show exactly when a follower fell behind and how long catch-up took.- Correlating leader write latency (
zk_avg_updatelatency,zk_p99_updatelatency) withzk_outstanding_requestsduring a SNAP sync tells you whether the leader’s pipeline is saturated by serialization work or merely busy. - Host-level disk and network metrics on the leader and follower, alongside ZooKeeper metrics, isolate whether the bottleneck is leader CPU, leader network output, follower disk, or follower deserialization.
- Anomaly detection on
zk_fsync_threshold_exceed_countandzk_digest_mismatches_countsurfaces the integrity risks described in the version-specific bugs above without waiting for a human to notice a counter moving.
Related guides
- ZooKeeper avg_latency hides write stalls: why the headline number lies
- ZooKeeper “Cannot open channel to N at election address”: the blocked election port
- ZooKeeper “Client session timed out, have not heard from server”: the heartbeat miss
- ZooKeeper connection drops spiking: sessions dying in bursts
- ZooKeeper “fsync-ing the write ahead log took too long”: the disk warning behind most write stalls
- How ZooKeeper actually works in production: a mental model for operators
- ZooKeeper leader election storm: an ensemble that keeps re-electing
- ZooKeeper monitoring checklist: the signals every production ensemble needs
- ZooKeeper monitoring maturity model: from survival to expert
- ZooKeeper outstanding requests growing: the request pipeline is backing up
- ZooKeeper proposals not committing: proposal_count outpacing commit_count
- ZooKeeper quorum ack latency high: followers slow to acknowledge proposals






