Summary
On shutdown, axum drains in-flight HTTP requests, but the background Horizon poller (and any in-flight webhook deliveries) are simply dropped when main returns.
Where
src/main.rs:36 — tokio::spawn(horizon::run_poller(state.clone())) — the JoinHandle is discarded.
src/main.rs:43 — with_graceful_shutdown(shutdown_signal()) only governs the HTTP server.
Impact
A deploy/restart during a poll cycle can abort a settlement write or a webhook mid-flight, leaving inconsistent state that relies on the next poll to self-heal (and, per #1, webhooks may not be re-driven at all today).
Proposed change
- Use a
CancellationToken (or a shutdown broadcast channel) shared with run_poller; have the poller select on it between cycles and exit cleanly.
- Keep the poller
JoinHandle and await it (with a timeout) after the server stops.
- Ensure an in-progress
poll_once is allowed to finish (or is cancelled at a safe point) rather than dropped.
Acceptance criteria
- SIGTERM/Ctrl-C stops the poller deterministically and logs a clean exit.
- Shutdown waits (bounded) for an in-flight poll cycle to complete.
- No new clippy warnings; existing tests pass.
Summary
On shutdown, axum drains in-flight HTTP requests, but the background Horizon poller (and any in-flight webhook deliveries) are simply dropped when
mainreturns.Where
src/main.rs:36—tokio::spawn(horizon::run_poller(state.clone()))— theJoinHandleis discarded.src/main.rs:43—with_graceful_shutdown(shutdown_signal())only governs the HTTP server.Impact
A deploy/restart during a poll cycle can abort a settlement write or a webhook mid-flight, leaving inconsistent state that relies on the next poll to self-heal (and, per #1, webhooks may not be re-driven at all today).
Proposed change
CancellationToken(or a shutdown broadcast channel) shared withrun_poller; have the poller select on it between cycles and exit cleanly.JoinHandleandawaitit (with a timeout) after the server stops.poll_onceis allowed to finish (or is cancelled at a safe point) rather than dropped.Acceptance criteria