Repository navigation
feat: Graceful shutdown to drain in-flight jobs on SIGTERM (AWOL plan, phases 1-2) - #161
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are identified for the entrypoint signal-handling change.
No open issues 🎉
What changed in this PR
Implements Phase 1 of the AWOL-on-rollout plan by letting Heimdall receive container SIGTERM directly. Graceful draining remains deferred to Phase 2.
Changes:
- Runs database deployment and Heimdall startup at the top level, using
execto replace the entrypoint shell. - Preserves background Next.js startup and prevents Heimdall from starting if database deployment fails.
| File | Description |
|---|---|
entrypoint.sh |
Removes the Heimdall startup subshell and uses exec so Heimdall becomes PID 1. |
Review effort: Balanced
Nitin Bhakar (nitinbhakar)
approved these changes
Nov 5, 2026
3 of 4 tasks
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.
AWOL-on-rollout plan
This PR is Phases 1 and 2.
execin entrypoint; ECSstopTimeout120s in deploy repo)plugin.Resumerforsparkeksandecs)unpicked_job_timeoutfor jobs never claimed, off by defaultProblem
During rollouts, async jobs running on the old task are failed with
awol: job is stale, marking failed. The janitor's pluginCleanupthen deletes their Spark apps or stops their ECS tasks, which were usually still healthy.Heimdall never saw the container's SIGTERM, and had no shutdown handling anyway.
entrypoint.shstarted it in a subshell, so bash was PID 1. ECS sends SIGTERM to PID 1, waitsstopTimeout, then SIGKILLs everything. Every in-flight job, sync request and log archive copy died at once.Changes
Phase 1:
entrypoint.shRuns
deploydband thenexec ./heimdallat the top level, so Heimdall replaces the shell as PID 1 and receives SIGTERM directly. Next.js still starts in the background as before.Phase 2: graceful drain
On the first SIGTERM or interrupt, each step waiting up to
shutdown_timeoutseconds. The default comes from theHEIMDALL_SHUTDOWN_TIMEOUTenvironment variable, which forklift sets fromstop_timeout(patterninc/forklift#152); without it, 30 seconds, the ECS default. Ashutdown_timeoutin the config file overrides both:new, and another task picks them up.http.Server.Shutdown: new connections are refused, and in-flight sync requests finish.A second signal exits immediately. If a step times out, Heimdall exits non-zero with
context deadline exceeded. Jobs still running at that point are handled by the janitor exactly as today; Phase 3 addresses them.Impact
insertJob, syncstoreResults). They're usually done within the drain, but one still running at exit is cut off as today.deploydbfails, the container still exits without starting Heimdall, as before.Follow-up outside this repo
Set
stop_timeout: 120(the Fargate maximum) in the deploy repo'sforklift.yaml: patterninc/heimdall-config#549. Forklift passes the same value in asHEIMDALL_SHUTDOWN_TIMEOUT, so the drain always matches the real stop timeout.Testing
Immediate escape when no pending jobs are there (immediately exiting)

Waiting for active jobs to drain (waiting for longer period)
