feat(cluster,compaction): cluster inspection + node removal, compaction observe/trigger (PR6) - #6
Merged
Merged
Conversation
…on observe/trigger (PR6)
arcli cluster {status,nodes,node show [--local],node remove,health} over
/api/v1/cluster/*. Standalone servers report "clustering: disabled"
from `status` (exit 0) and an error from everything else. Node removal
resolves the raft leader to its API address when refused by a follower.
arcli compaction {status,stats,candidates,history,trigger [--wait]}
over /api/v1/compaction/*. A "route missing" 404 is reported as
compaction disabled only after /health proves the host is Arc. --wait
polls until the expected cycle finishes and gives up early when the
server's predicted cycle id never materialises. /compaction/jobs is a
server stub and is not exposed.
This was referenced Sep 7, 2026
This was referenced Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
arcli cluster {status,nodes,node show [--local],node remove,health}over/api/v1/cluster/*. Arc registers these routes on every server and answers{"enabled":false,"mode":"standalone","reason":...}(HTTP 200) without a coordinator;cluster statusreports that as a normal result (exit 0, raw JSON passed through), every other subcommand exits 1 with the reason so scripts never mistake "no cluster" for an empty cluster.cluster node remove(admin): pre-flightGET /clusterrefuses self-removal client-side and tells the operator when raft is off (registry-only removal) or when this node is not the leader. On the server's "not the leader" refusal arcli re-fetches the status and names the leader's API address (resolved fromraft.leader_id→nodes[].api_address, neverraft.leader_addr, which is the raft transport port). arcli never retries against another host. Node ids are validated (1..256, no./.., no/ ? # %, whitespace, or control chars) and percent-encoded.arcli compaction {status,stats,candidates,history,trigger}over/api/v1/compaction/*. Compaction routes only exist whencompaction.enabled=true; Fiber's "Cannot GET" 404 is reported as "compaction is disabled" only after the strict/healthcheck proves the host is Arc, otherwise the 404 is surfaced with "check the endpoint path".compaction trigger: query-string API (server ignores bodies), client-side tier enum and database-name grammar (server validates neither), warning for tiers disabled or unconfigured server-side (Arc skips them silently), 409 decoded into "a compaction cycle is already running (cycle N)".--waitpollsstatsuntil the expected cycle finishes; because the server'scycle_idis a prediction read after the goroutine is spawned,--waitgives up after a few idle polls below the expected id instead of sleeping to--wait-timeout.-o json --waitkeeps stdout as a single JSON document./compaction/jobs(server stub, alwaysactive_jobs: 0, jobs: []),cluster files(O(N) manifest; later).never/-, binary byte units,SAVED%as fraction removed,RESULTshowsfailed: <error>; JSON output re-indents the raw server body so key order and null-vs-absent are preserved. Server-supplied strings are scrubbed of control characters before reaching the terminal.Test plan
gofmt -l .empty,go vet ./...,go test -race -count=1 ./...greenraft.stats: null,license.features: null, zero timestamps; role/state enum; node id validation + path escaping; leader hint uses API address;cluster status -o jsonraw pass-through; compaction disabled vs wrong-base-path vs handler 404;active_jobs: null,next_runwith local offset → UTC, zeronext_run;tiersabsent; 409 cycle id;--waitcompletes, keeps JSON stdout clean, and gives up when the server idles below the expected id; advisory tier pre-flight; unconfigured tier warningarc serve(HEAD e5c3f5e, standalone, auth on): 25/25 — cluster status exit 0 + JSON, other cluster commands exit 1 with the license reason, compaction status/stats/candidates/history (+--limitnote), trigger +--wait, invalid tier/database client-side, non-admin trigger 403 while status is allowed, wrong base path not misreported, first-run errorARC_COMPACTION_ENABLED=false: all five compaction commands report disabled, exit 1coordinator.Status(),nodeToMap,HealthChecker.Status())Review notes
Internal: adversarial plan review (leader hint corrected to API address,
/health-gated disabled detection, raw pass-through for standalone status,--waitand unconfigured-tier warnings); deep diff review (2 High fixed:--waitcould sleep to the deadline on the server's cycle-id race;-o json --waitpolluted stdout); security review (checklist all PASS; 3 missed control-char scrubs, node id validation, hint wording). Deferred follow-ups: signal-aware root context so--waitcan report on ctrl-C (touchesmain.go); Arc-side: trigger handler should read the cycle id before spawning the goroutine.