feat(k8s): graceful-lifecycle hardening -- startupProbe, preStop, drain-readiness (P0-2/3/4/5)#745
Conversation
…in-readiness (P0-2/3/4/5) The remaining k8s-readiness P0 fixes (plan in deploy/K8S_READINESS_PLAN.md): - P0-5 (server): /readyz returns 503 the moment graceful shutdown begins, so k8s deprograms this pod's Service endpoint BEFORE the drain -- no client routed to a draining node. MetricsState gains an optional shutdown flag (from set.shutdown_flag(), the #543 Arc<AtomicBool>); readiness() checks it FIRST. Unit-tested (ready -> drain flips /readyz 200 -> 503 draining). Belt-and-suspenders with the preStop below, and the fallback where SleepAction is unavailable. - P0-2 (chart): startupProbe on /readyz suspends liveness+readiness until load-on-boot completes, so a slow multi-GB snapshot RELOAD does not trip the ~65s liveness budget and CrashLoopBackOff a healthy node. Liveness/readiness lose their now-redundant initialDelaySeconds. failureThreshold*periodSeconds (default 60*10 = 10 min) is a values knob. C3 verified: the metrics listener binds while load-on-boot runs in the shard tasks, so /readyz reports load-incomplete during the reload. - P0-3 (chart): preStop lame-duck via the NATIVE SleepAction (k8s 1.29+) -- NOT exec sleep, which would fail on the shell-free distroless image. Gated on preStop.sleepSeconds > 0 (0 disables it on older k8s -> rely on P0-5). kubeconform -strict against KUBE_VERSION 1.30 accepts the field (SleepAction GA). - P0-4 (chart): terminationGracePeriodSeconds documented to cover preStop + drain + save-on-exit, sized from the same dataset estimate as the startupProbe budget; guidance to raise it for large datasets. Applied to BOTH the Helm chart (values-tunable) and deploy/k8s/ironcache.yaml (static, for parity). All metrics + shutdown tests, clippy -D warnings, fmt, dash clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FfFZ8gkkNhDBASuntB72HR
Both reviewers SHIP. Reviewer 1 (server) verified /readyz reads the SAME
Arc<AtomicBool> that SIGTERM/shutdown_and_join/committed-cutover all set (traced
from its single creation in bootstrap.rs) -- P0-5 is live, not inert; /livez stays
untouched (no SIGKILL risk); the C3 async-load claim holds. No defects.
Reviewer 2 (chart) findings folded in:
- [MEDIUM] preStop defaulted ON would be rejected by the API server on k8s < 1.29.
Gate the lifecycle block on semverCompare ">=1.29-0" .Capabilities.KubeVersion so
the chart SELF-PROTECTS on old clusters (silently degrades to the /readyz-on-drain
fallback) instead of a broken install. deploy-lint's `helm template` now pins
--kube-version "${KUBE_VERSION}" (1.30) so the gated block deterministically renders
and is kubeconform-validated in CI.
- [LOW] values.yaml notes the startupProbe budget must also cover leader-election time
in raft mode (cold cluster-wide restart), not just reload time.
- Corrected the SleepAction version phrasing (alpha 1.29 / beta+default-on 1.30 / GA 1.34).
Chart-only + workflow; no code change from the reviewed server logic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfFZ8gkkNhDBASuntB72HR
2-lens review: both SHIP; findings folded inLens 1 (server correctness): SHIP, no defects. Decisively confirmed the load-bearing question -- Lens 2 (chart/k8s): SHIP. kubeconform-pass verified (1.30 schema has
All metrics (17) + shutdown (9) tests, clippy |
perf-gate (A5)Same-runner ratchet of HEAD against the merge-base (both rebuilt and measured in this job).
Overall: WARN
|
…ss plan (#754) Adds a "1a. Implementation status" block after the executive summary summarizing the shipped P0 (#743/#745) + full P1 tier (13/13, #746-753) with PR refs, so the plan no longer reads as if that work is still MISSING/PARTIAL (the inline table markers are kept as the pre-work baseline for context). Also records the two draft claims that code fact-checks corrected during implementation (helm-upgrade does NOT silently corrupt a reshard; REBALANCE APPLY is implemented). Notes the chart is now production-safe for fixed-size clusters; only P2 packaging polish + the day-2 Operator remain. Claude-Session: https://claude.ai/code/session_01FfFZ8gkkNhDBASuntB72HR Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The remaining k8s-readiness P0 fixes (after #743's OOM guard). Plan:
deploy/K8S_READINESS_PLAN.md.Changes
metrics_http.rs+main.rs):/readyzreturns 503 the moment graceful shutdown begins, so k8s deprograms this pod's endpoint before the drain -- no client routed to a draining node.MetricsStategains an optional shutdown flag (set.shutdown_flag(), the bug: graceful shutdown can hang joining a shard with a parked subscriber connection (flaky keyspace_notifications teardown) #543Arc<AtomicBool>);readiness()checks it first. Unit-tested (ready -> drain flips/readyz200 -> 503draining).startupProbeon/readyzsuspends liveness+readiness until load-on-boot completes -- a slow multi-GB snapshot RELOAD no longer trips the ~65s liveness budget and CrashLoopBackOffs a healthy node.failureThreshold*periodSeconds(default 60*10 = 10 min) is a values knob. C3 verified: the metrics listener binds while load-on-boot runs in the shard tasks, so/readyzreports load-incomplete during the reload.preStoplame-duck via the nativeSleepAction(k8s 1.29+) -- NOTexec sleep, which would fail on the shell-free distroless image. Gated onpreStop.sleepSeconds > 0(0 disables it on older k8s -> rely on P0-5). kubeconform-strict@KUBE_VERSION1.30 accepts it (SleepAction GA).terminationGracePeriodSecondsdocumented to cover preStop + drain + save-on-exit, sized from the same dataset estimate as the startupProbe budget.Applied to BOTH the Helm chart (values-tunable) and
deploy/k8s/ironcache.yaml(static, parity).Tests
/readyz-drain unit test + all metrics (17) & shutdown (9) tests green, clippy-D warnings, fmt, dash clean. deploy-lint (helm template | kubeconform -strict @ 1.30) exercises the rendered chart.🤖 Generated with Claude Code