Skip to content

feat(chart): make s3proxy probes configurable (tolerant liveness)#95

Merged
ServerSideHannes merged 1 commit into
mainfrom
fix/offload-cpu-bound-work-off-event-loop
Jun 30, 2026
Merged

feat(chart): make s3proxy probes configurable (tolerant liveness)#95
ServerSideHannes merged 1 commit into
mainfrom
fix/offload-cpu-bound-work-off-event-loop

Conversation

@ServerSideHannes

@ServerSideHannes ServerSideHannes commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Why

Under backup upload load the s3proxy pods entered a self-sustaining CrashLoopBackOff cascade: Container … failed liveness probe, will be restarted (pods serving fine then SIGTERM'd, exit 0) → in-flight uploads dropped → barman/scylla retried → backlog grew → next pod hit harder → cascade. It persisted after removing the scylla workload and was not fixed by raising the memory governor.

Root mechanism: the proxy runs a single uvicorn worker (one asyncio event loop), so under load /healthz can't be served within the 5 s liveness timeout, and kubelet kills a live pod. The probes were hardcoded in the deployment template — no way for a deployment to loosen them.

What

Expose the s3proxy container's startupProbe / livenessProbe / readinessProbe via .Values (defaults = current values, rendered with toYaml). Helm merges maps, so a deployment can set just livenessProbe.timeoutSeconds: 60 without restating the whole probe. This lets argocd raise the liveness timeout to absorb a busy loop instead of restarting it.

helm lint clean; helm template --set livenessProbe.timeoutSeconds=60 renders the merged probe correctly.

Note — the crypto-offload approach was dropped

The earlier version of this PR offloaded AES-GCM to asyncio.to_thread. Empirically that does not help: cryptography's AES-GCM holds the GIL, so the worker thread blocks the loop thread anyway. Measured loop ticks during a 110 ms encrypt: baseline idle 104, inline 9, to_thread 10 — i.e. to_thread ≈ inline. So the real levers are probe tolerance (this PR) + capacity (workers/CPU, deploy-side, needs a matching pod-memory bump to avoid OOM), not offload.

@ServerSideHannes ServerSideHannes force-pushed the fix/offload-cpu-bound-work-off-event-loop branch from c4dcab7 to 000a9cb Compare June 30, 2026 11:58
@ServerSideHannes ServerSideHannes changed the title fix: offload whole-buffer crypto off the event loop (stops liveness-kill crashloop) + backoff redis WATCH contention fix: offload whole-buffer crypto off the event loop + run 2 uvicorn workers (stops liveness-kill crashloop) Jun 30, 2026
@ServerSideHannes ServerSideHannes force-pushed the fix/offload-cpu-bound-work-off-event-loop branch from 000a9cb to c498991 Compare June 30, 2026 11:59
@ServerSideHannes ServerSideHannes changed the title fix: offload whole-buffer crypto off the event loop + run 2 uvicorn workers (stops liveness-kill crashloop) fix: offload whole-buffer crypto off the event loop (stops liveness-kill crashloop) Jun 30, 2026
…a busy loop)

The s3proxy container's startup/liveness/readiness probes were hardcoded in the
deployment template, so a deployment couldn't loosen them. The proxy runs a
single uvicorn worker (one asyncio event loop); under heavy upload load a probe
can take seconds to be served, and the tight 5s liveness timeout then restarts a
live pod -> in-flight uploads drop -> clients retry -> load spiral -> cluster-wide
crashloop (observed in production under backup load).

Expose all three probes via .Values.{startupProbe,livenessProbe,readinessProbe}
with the current values as defaults (toYaml; maps merge, so a deployment can set
just e.g. livenessProbe.timeoutSeconds without restating the probe). Lets
argocd raise the liveness timeout to absorb a busy loop instead of killing it.

(Drops the earlier crypto-offload approach: empirically, cryptography's AES-GCM
holds the GIL, so asyncio.to_thread did not free the event loop -- to_thread and
inline gave the same ~9-10 loop ticks during a 110ms encrypt. The real lever is
probe tolerance + capacity, not offload.)
@ServerSideHannes ServerSideHannes force-pushed the fix/offload-cpu-bound-work-off-event-loop branch from c498991 to f7a6fad Compare June 30, 2026 12:08
@ServerSideHannes ServerSideHannes changed the title fix: offload whole-buffer crypto off the event loop (stops liveness-kill crashloop) feat(chart): make s3proxy probes configurable (tolerant liveness) Jun 30, 2026
@ServerSideHannes ServerSideHannes merged commit 381ac2c into main Jun 30, 2026
4 checks passed
@ServerSideHannes ServerSideHannes deleted the fix/offload-cpu-bound-work-off-event-loop branch June 30, 2026 12:13
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