Skip to content

keeper: Raft log position and snapshot settings from every member, asked in parallel; P-61 - #56

Merged
CamiloSierraH merged 5 commits into
mainfrom
cami/keeper-log-config
Oct 20, 2026
Merged

CamiloSierraH merged 5 commits into
mainfrom
cami/keeper-log-config

Conversation

@CamiloSierraH

@CamiloSierraH CamiloSierraH commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Why

A recent sev-1 Keeper quorum loss turned on two facts the bundle did not carry:

  1. One member had fallen behind the leader's retained log, so it could rejoin only through a full snapshot.
  2. That snapshot was over 2 GiB and sent unchunked (snapshot_transfer_chunk_size = 0, the default). Past 2 GiB the transfer fails on every leader in turn (std::bad_alloc), and the ensemble ended up with no leader.

Both facts are one four-letter word away (lgif, conf), from any ClickHouse node, with no access to the Keeper hosts.

What changed

The collector sends seven read-only commands per member instead of three:

Command Answers when not serving? Gives
ruok yes liveness
isro yes ro/rw
lgif yes Raft log position: first/last index, committed vs leader-committed, last snapshot index
srvr, mntr no as before, including zk_latest_snapshot_size
conf no snapshot_transfer_chunk_size, write_snapshot_version, snapshot_distance, reserved_log_items … (in gov only numbers, booleans and plain words survive; paths, hosts, *_path, *_disk, dataDir, dataLogDir and server.N say REMOVED)
dirs no bytes in the snapshot and log directories

The ones that answer on a member that is not serving go first, because that is the state of a quorum loss. Keeper checks isServerActive() only for the others; I read this from its source at 26.8. Commands with side effects (csnp, rqld, ydld, rcvr, srst, crst, rclc, clrs) are never sent. Neither are those that list clients (cons, stat, dump, wchc, wchp).

  • Parallel, fail-fast. Members are asked in parallel, and a member is asked nothing more after its first network failure. A member that accepts connections but never answers (seen in that incident) used to cost the 10 s read timeout once per command.
  • "Not serving" is a status. A member answering This instance is not currently serving requests used to be filed as ok.
  • Older Keepers stay ok. A command that an older Keeper lacks (lgif on 22.8) no longer makes the member partial; status is judged on ruok/srvr/mntr as before.

The pre-pass reads keeper/ for the first time:

  • HC-3.18: availability from Keeper's own replies, per ensemble. It is critical only on the members' own word: two or more answer not currently serving requests and none is serving. A member not serving, or whose probe failed on the collector's side, is a warning that does not judge the quorum. The files hold the members this server knows, not proven voting membership.
  • HC-3.19: within one ensemble, a member whose last_log_idx is below the observed leader's first_log_idx needs a full snapshot. Critical. Without a leader reply there is no verdict, because followers compact their logs independently. A truncated reply is never read.
  • HC-3.20: a snapshot over 2 GiB with snapshot_transfer_chunk_size = 0 is critical (over 1 GiB, a warning). write_snapshot_version below 7, where sequential counters are still 32-bit, is a warning on 26.8+, where 8 is the default so a lower value was pinned. Before 26.8, where 6 is the default, it is info.
  • With no conf in the file there is no finding: the chunk size is never assumed. Snapshot size and chunk setting are compared within one ensemble, and the format is judged per member against its own version.
  • Ensembles are kept apart. system.zookeeper_connection lists the default connection and every auxiliary ZooKeeper, so each file now carries # ensemble: <name> (hashed in gov), and cross-member checks never mix ensembles. An auxiliary member's file is <ensemble>__<host>_<port>.txt, so a shared host:port never collides. Auxiliary blocks are never read as the default ensemble, even one named zookeeper.
  • P-61 describes the pattern and the fix: keep the lagging member stopped, restart the up-to-date ones together, set snapshot_transfer_chunk_size and nuraft_snapshot_sync_ctx_timeout_ms on every member, and only then start the lagging one.

Measured (lab: 26.7, 3-node Keeper, 4 servers)

before after
Healthy ensemble, Keeper step ~1 ms per member 10 ms total, 7 commands × 3 members
One member frozen (docker pause: accepts, never answers) 30.02 s 10.01 s
Each command, median / max over 60 calls 0.25–0.33 ms / ≤ 1.9 ms, replies ≤ 2.7 KB

The commands do not grow with the data size: lgif reads counters, and dirs sums a few dozen files. That part comes from the source and was not measured on a 100M-znode Keeper.

Verification

  • Collector tests: command order; stop after the first timeout, with the remaining commands written as skipped; members in parallel (three hung members fit inside one timeout); not serving; an older Keeper still ok; gov conf paths removed. Each one fails when its behaviour is reverted.
  • Pre-pass tests: the quorum-loss shape (a 2.2 GiB unchunked snapshot, a member behind the retained log, format 6 pinned on 26.8, 2 of 3 serving is a warning, not critical); 1 of 3 serving is critical; a healthy ensemble with the fixes applied raises nothing; an older bundle without conf claims nothing; 26.7's default format is info. Each rule has a reverted-code mutation that fails its test.
  • Lab, real binary:
    • The healthy run gives three members serving, one leader, and no false findings.
    • The run with a frozen member gives unreachable, as a 2-of-3 warning.
    • The 26.7 default format gets info.
  • 22.8 Keeper: every new command exists except lgif, which returns an empty reply, now handled.
  • Review round: each fix has a test that failed against the earlier code, and a mutation that reverts it fails that test. The fixes cover truncated replies, collector failures asserting an outage, mixed ensembles, a silent failed probe and the ensemble-wide version.
  • From dashboard: a Keeper members panel — role, Raft log position and snapshot settings per member #58's review: Apache ZooKeeper's "This ZooKeeper instance is not currently serving requests" is recognised like Keeper's wording. A ZooKeeper member that was not serving used to come out ok. HC-3.19 requires exactly one serving leader, because mid-election two can report leader and the verdict depended on file order.
  • Second review round: gov conf identifiers (Keeper and ZooKeeper reply formats), a shared endpoint across ensembles, a nested auxiliary zookeeper block, and no snapshot verdict without a leader. Each has a regression test, and each rule fails its test when reverted.
  • gofmt, go vet (also GOOS=windows) and make test are green, with main (cluster: collect every host of a self-managed cluster from one command #52, archive: written to a temporary file and renamed into place only when complete; a failed archive exits 1 #53) merged in: 287 passed. 11 files: running-the-tool.md was updated with approval to exceed the usual 10.

Not covered: Keeper's own log files, which live on the Keeper hosts. The decisive bad_alloc lines are there, and the tool does not run on those hosts. That needs a separate Keeper-host collection.

🤖 Generated with Claude Code

…ked in parallel; P-61

A recent sev-1 Keeper quorum loss turned on two facts the bundle did not
carry: one member had fallen behind the leader's retained log and could
rejoin only through a full snapshot, and that snapshot (over 2 GiB) was sent
unchunked (snapshot_transfer_chunk_size = 0), which fails past 2 GiB on
every leader in turn. Both are one four-letter word away.

The collector now sends ruok, isro, lgif, srvr, mntr, conf, dirs — all
read-only, ~0.3 ms each, none through Raft or growing with the data size.
isro and lgif answer even on a member that is not serving, so they go
first. Members are asked in parallel and a member gets nothing more after
its first network failure: one frozen member cost 30 s before (three
commands at the 10 s read timeout), 10 s now, however many hang. A member
answering "This instance is not currently serving requests" is reported
"not serving" instead of ok; a newer command absent on an older Keeper
(lgif on 22.8) does not lower a member's status; gov removes conf's
storage paths.

The pre-pass reads keeper/ for the first time: HC-3.18 (quorum from
Keeper's own replies), HC-3.19 (a member behind the leader's retained
log), HC-3.20 (an unchunked snapshot over 2 GiB; snapshot format below 7 —
a warning only on 26.8+, where 8 is the default) and P-61 with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Ensemble assumptions and reply handling can produce false critical diagnoses or hide probe failures.

5 open issues
What changed in this PR

Expands Keeper evidence collection and bundle analysis to diagnose quorum problems and snapshot-transfer failures.

Changes:

  • Collects seven read-only commands per member, in parallel, stopping after network failures.
  • Adds Keeper availability, Raft-log and snapshot-setting checks.
  • Adds regression tests and documents P-61 recovery guidance.
File Description
skills/​clickhouse-diagnostic/​scripts/​inspect_bundle.py Parses Keeper replies and adds health checks.
skills/​clickhouse-diagnostic/​references/​known-patterns.md Adds P-61 snapshot-transfer guidance.
skills/​clickhouse-diagnostic/​references/​health-checks.md Documents HC-3.18–3.20.
skills/​clickhouse-diagnostic/​references/​file-guide.md Explains new Keeper evidence and limitations.
skills/​clickhouse-diagnostic/​references/​bundle-layout.md Documents reply sections and statuses.
README.md Updates collection behavior and command documentation.
internal/​keeper/​fourletter.go Expands commands, parallelizes collection and filters gov paths.
internal/​keeper/​fourletter_test.go Tests collection behavior and filtering.
cmd/​prepass_keeper_test.go Adds Keeper analysis fixtures and assertions.
cmd/​main.go Updates CLI help and collection messages.

Review effort: Balanced

Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
…a time, truncated replies are not evidence

- A truncated reply (partial text, then "error: timeout … reply
  truncated") was read as an answer: a cut-off last_log_idx could raise a
  false "behind the retained log" critical. Any section with a collector
  error line is now not evidence.
- The files hold the members this server knows, not proven voting
  membership, and a collector timeout says nothing about the voters' own
  links: "0 of 1 serving — no quorum" on a remote run was wrong. A
  critical now needs two or more members answering "not serving"
  themselves and none serving; a failed probe is a warning that says
  quorum is not judged.
- A member whose probe failed after ruok (lgif or srvr timing out, the
  rest skipped) was "unknown" and silent; it is now "probe failed".
- system.zookeeper_connection lists the default connection and every
  auxiliary ZooKeeper. The collector now records the connection name
  ("# ensemble:", hashed in gov when not default), and the log, snapshot
  and chunk comparisons run within one ensemble; without the line each
  member is judged on its own figures.
- The snapshot format is judged per member against its own version, so a
  member pinned on 26.8 during a rolling upgrade warns while a 26.7 member's
  default is info; identical verdicts are one finding.
- running-the-tool.md's Keeper recipe lists the new commands and checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread internal/keeper/fourletter.go
Comment thread internal/keeper/fourletter.go Outdated
Comment thread internal/keeper/fourletter.go Outdated
Comment thread skills/clickhouse-diagnostic/scripts/inspect_bundle.py Outdated
CamiloSierraH and others added 2 commits October 19, 2026 13:52
…oints get two files, no snapshot verdict without the leader

- Gov conf filtered only *_path values. Apache ZooKeeper's dataDir,
  dataLogDir and server.N (ensemble hosts and ports), host-shaped values and
  a customer-chosen *_disk name passed. Now only numbers, booleans and plain
  words survive; the setting stays with its value REMOVED. Tested with a
  Keeper and a ZooKeeper reply, including a host-only value the address
  filter cannot see.
- A default and an auxiliary connection can share a host:port (different
  chroots) and both wrote <host>_<port>.txt. An auxiliary member's file is
  <ensemble>__<host>_<port>.txt; a default member keeps the plain name.
- An auxiliary ZooKeeper named "zookeeper" inside <auxiliary_zookeepers>
  was read as the default ensemble. Auxiliary blocks are removed before
  the default <zookeeper> is read.
- Without an observed leader, a follower's first_log_idx says nothing about
  what the leader retains — followers compact independently. HC-3.19 now
  gives a "needs a full snapshot" verdict only against the leader's own
  lgif; during a fully leaderless outage it stays silent (HC-3.18 reports
  that state) rather than risk telling an operator to stop a member that
  could still catch up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dict with two leaders

Apache ZooKeeper answers "This ZooKeeper instance is not currently serving
requests". Only Keeper's exact sentence was recognised, so a ZooKeeper
member that was not serving came out "ok" in the collector and "serving"
in the pre-pass — hiding HC-3.18. Both wordings are recognised now.

Mid-election a bundle can hold two members reporting leader. The pre-pass
took the first in file order, so the HC-3.19 verdict depended on which file
sorted first. A "behind the leader's log" verdict now needs exactly one
serving leader; with two there is none — the same reasoning as no leader.
Found reviewing #58, which reads these files with the same rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CamiloSierraH added a commit that referenced this pull request Oct 19, 2026
… keeper-members-panel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CamiloSierraH added a commit that referenced this pull request Oct 19, 2026
…eader, and its banner claims only what the bundle shows

- Apache ZooKeeper answers "This ZooKeeper instance is not currently
  serving requests"; the Go reader matched only Keeper's sentence and
  showed such a member as serving. Both wordings count (shared
  keeper.IsNotServing, fixed in the collector and the pre-pass on #56).
- With two members reporting leader mid-election, the Go reader took the
  last and the pre-pass the first, so the two could disagree on HC-3.19.
  Both now require exactly one serving leader, and give no "behind the
  leader" verdict otherwise.
- The banner said the ensemble had no leader and writes fail. Two members
  not serving can sit next to voters this server does not know that hold a
  quorum; it now says no member in the bundle is serving, and that members
  outside it may still hold a quorum — the pre-pass's qualification.

The consistency test gains a ZooKeeper and a two-leader fixture; both
caught the readers disagreeing before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CamiloSierraH
CamiloSierraH requested a review from e-mars October 19, 2026 13:10
@CamiloSierraH
CamiloSierraH merged commit b1749db into main Oct 20, 2026
2 checks passed
CamiloSierraH added a commit that referenced this pull request Oct 20, 2026
…' dashboard tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants