fix(worker): thread-based liveness server so sync Bedrock signing cannot SIGKILL a busy worker#2906
Closed
Fyko wants to merge 1 commit into
Closed
fix(worker): thread-based liveness server so sync Bedrock signing cannot SIGKILL a busy worker#2906Fyko wants to merge 1 commit into
Fyko wants to merge 1 commit into
Conversation
…gning cannot SIGKILL a busy worker
Fyko
marked this pull request as ready for review
July 22, 2026 17:51
Collaborator
|
Followed up on the root-cause question with a visibility-first approach in #2942 — instead of a separate heartbeat liveness server, it adds two always-on signals so a stalled
Context that pushed me toward visibility over a workaround: on the pinned litellm, boto3/Bedrock credential resolution is already offloaded to a thread ( |
Contributor
Author
|
thank you @nicoloboschi! |
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.
Problem
The dedicated worker runs its poller and its async
/healthHTTP server on the same asyncio event loop. During Bedrock inference, litellm'sacompletionperforms synchronous botocore credential resolution + SigV4 signing on the loop thread, which starves the loop for several seconds. A KuberneteslivenessProbepointed at/healththen times out and the pod is SIGKILLed (exitCode 137).A loop blocked by sync signing is busy, not dead — liveness should not kill it.
Fix
Add a tiny liveness server (
worker/liveness.py) on a separate daemon thread with its own stdlibhttp.server:Heartbeat~1x/sec.200while the heartbeat is fresher than a staleness threshold,503once it goes stale.livenessProbepoints at this new port; a multi-second sync block goes momentarily stale but the loop resumes and the heartbeat catches up well within the threshold, so the probe stays green. A genuinely wedged loop never catches up and the probe fails, letting Kubernetes restart the pod.Why liveness-only (not the DB check)
The asyncpg pool is bound to the main event loop and cannot be
awaited from another thread. So the thread server serves liveness from a plain in-process signal (the heartbeat), never the DB. The existing async/healthkeeps the DB check and remains the readiness signal.Config
HINDSIGHT_API_WORKER_LIVENESS_PORT(default8890) — liveness port, next to the existing metrics/health port8889.HINDSIGHT_API_WORKER_LIVENESS_THRESHOLD_SECONDS(default30) — heartbeat staleness budget; comfortably exceeds worst-case sync-signing block while still catching a wedged loop.Both follow the existing
config.pyenv/default/dataclass/from_envconventions. A--liveness-portCLI flag mirrors--http-port.Tests
tests/test_worker_liveness.pycovers the heartbeat-staleness predicate (fresh / stale / multi-second-block-tolerant) andHeartbeatage reset.Scope
This is the worker-code side of the fix. It complements the chart-side port wiring in #2904 (which points the
livenessProbeat the new port) but is independently mergeable — this PR adds the server and config; the chart PR consumes them.