application: add graceful shutdown config for Bandit

Configures Bandit with 615s drain timeout and 620s OTP shutdown window
so in-flight Claude CLI calls (up to 600s) complete before systemd kills
the process on restart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Opus 4.6 2026-05-18 21:31:47 +00:00
parent 11a9026597
commit 244feb4d7e

View File

@ -12,9 +12,26 @@ defmodule Symbiont.Application do
Bandit (Symbiont.API) HTTP API Bandit (Symbiont.API) HTTP API
In test mode, the supervisor starts empty tests manage their own processes. In test mode, the supervisor starts empty tests manage their own processes.
## Graceful shutdown
Bandit is configured with a long ThousandIsland `shutdown_timeout` so that
in-flight `/task` requests (which can take up to 600s while Claude runs)
are not killed when systemd restarts the service. The Bandit child spec
also carries a matching `:shutdown` value so the OTP supervisor itself
waits long enough before forcing a kill.
The matching systemd unit must set `TimeoutStopSec=620` for this to take
effect end to end.
""" """
use Application use Application
# 615s for Bandit to drain its sockets, 620s for the OTP child shutdown
# window. Keep these slightly larger than the Claude CLI timeout (600s)
# used by Muse's SMTP dispatcher.
@bandit_drain_ms 615_000
@bandit_child_shutdown_ms 620_000
@impl true @impl true
def start(_type, _args) do def start(_type, _args) do
if Application.get_env(:symbiont, :port) == 0 do if Application.get_env(:symbiont, :port) == 0 do
@ -32,13 +49,23 @@ defmodule Symbiont.Application do
port = Application.get_env(:symbiont, :port, 8111) port = Application.get_env(:symbiont, :port, 8111)
engram_db = Application.get_env(:symbiont, :engram_db, "/data/symbiont/engram.db") engram_db = Application.get_env(:symbiont, :engram_db, "/data/symbiont/engram.db")
bandit_spec =
Supervisor.child_spec(
{Bandit,
plug: Symbiont.API,
port: port,
scheme: :http,
thousand_island_options: [shutdown_timeout: @bandit_drain_ms]},
shutdown: @bandit_child_shutdown_ms
)
children = [ children = [
{Task.Supervisor, name: Symbiont.TaskSupervisor}, {Task.Supervisor, name: Symbiont.TaskSupervisor},
{Symbiont.Ledger, data_dir: data_dir}, {Symbiont.Ledger, data_dir: data_dir},
{Symbiont.Queue, data_dir: data_dir}, {Symbiont.Queue, data_dir: data_dir},
{Symbiont.Engram, db_path: engram_db}, {Symbiont.Engram, db_path: engram_db},
{Symbiont.Heartbeat, []}, {Symbiont.Heartbeat, []},
{Bandit, plug: Symbiont.API, port: port, scheme: :http} bandit_spec
] ]
opts = [strategy: :rest_for_one, name: Symbiont.Supervisor] opts = [strategy: :rest_for_one, name: Symbiont.Supervisor]