Skip to content

feat(cluster,compaction): cluster inspection + node removal, compaction observe/trigger (PR6) - #6

Merged
xe-nvdk merged 1 commit into
mainfrom
feat/pr6-cluster-compaction
Sep 7, 2026
Merged

xe-nvdk merged 1 commit into
mainfrom
feat/pr6-cluster-compaction

Conversation

@xe-nvdk

@xe-nvdk xe-nvdk commented Sep 7, 2026

Copy link
Copy Markdown
Member

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 status reports 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-flight GET /cluster refuses 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 from raft.leader_id → nodes[].api_address, never raft.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 when compaction.enabled=true; Fiber's "Cannot GET" 404 is reported as "compaction is disabled" only after the strict /health check 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)". --wait polls stats until the expected cycle finishes; because the server's cycle_id is a prediction read after the goroutine is spawned, --wait gives up after a few idle polls below the expected id instead of sleeping to --wait-timeout. -o json --wait keeps stdout as a single JSON document.
  • Not exposed: /compaction/jobs (server stub, always active_jobs: 0, jobs: []), cluster files (O(N) manifest; later).
  • Table rendering: UTC timestamps, zero times as never/-, binary byte units, SAVED% as fraction removed, RESULT shows failed: <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 ./... green
  • Unit: standalone sentinel on all cluster methods incl. DELETE; enabled-cluster decode with raft.stats: null, license.features: null, zero timestamps; role/state enum; node id validation + path escaping; leader hint uses API address; cluster status -o json raw pass-through; compaction disabled vs wrong-base-path vs handler 404; active_jobs: null, next_run with local offset → UTC, zero next_run; tiers absent; 409 cycle id; --wait completes, keeps JSON stdout clean, and gives up when the server idles below the expected id; advisory tier pre-flight; unconfigured tier warning
  • Smoke against arc 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 (+--limit note), trigger + --wait, invalid tier/database client-side, non-admin trigger 403 while status is allowed, wrong base path not misreported, first-run error
  • Smoke with ARC_COMPACTION_ENABLED=false: all five compaction commands report disabled, exit 1
  • Enabled-cluster path: no enterprise-licensed cluster available locally; covered by httptest fakes built from the verbatim server shapes (coordinator.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, --wait and unconfigured-tier warnings); deep diff review (2 High fixed: --wait could sleep to the deadline on the server's cycle-id race; -o json --wait polluted stdout); security review (checklist all PASS; 3 missed control-char scrubs, node id validation, hint wording). Deferred follow-ups: signal-aware root context so --wait can report on ctrl-C (touches main.go); Arc-side: trigger handler should read the cycle id before spawning the goroutine.

…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.
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.

1 participant