fix: back off cron-worker restarts so a DB blip can't cascade into a fleet stall#1159
Draft
frol-ai wants to merge 1 commit into
Draft
fix: back off cron-worker restarts so a DB blip can't cascade into a fleet stall#1159frol-ai wants to merge 1 commit into
frol-ai wants to merge 1 commit into
Conversation
A cron worker exits when its CronStream->store pipe errors (a DB connection reset kills the tick push), and the Monitor rebuilds it *immediately* with no delay. During a persistent DB outage that is a hot restart loop: spins CPU, hammers the DB with reconnect attempts, and starves every other worker on the shared runtime — how a brief DB blip cascaded into a fleet-wide stall that never recovered on its own. Prepend an async backoff to the cron feed on restart (attempt > 0): 2s, 4s, … capped at 60s. The delay yields no ticks, so no DB push is attempted until it elapses. The worker now quietly backs off and retries until the DB is back and resumes on its own — no process restart needed. (The bronze queue workers already self-recover via the poll fetcher's own backoff; only the cron pipe lacked this.) Complements the liveness self-heal (last resort) and pool hardening. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRWEKUFYCcmGGhwdDYd471
frol
marked this pull request as draft
July 22, 2026 11:59
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.
The catastrophic, non-recovering behavior in the logs traces to a hot restart loop.
Mechanism
A cron worker's backend is
CronStream::new(schedule).pipe_to(store). apalis's pipe pushes ticks with aoncefuture runningsend_all— a single store-push error ends it permanently. So when a DB connection reset kills a tick push, the pipe stream yields an error → the worker exits withWorkerError::StreamError. The Monitor's restart loop then rebuilds the worker immediately, with no delay (verified inapalis-coremonitor::mod). During a persistent DB outage this is a tight loop: rebuild → push a tick → DB still down → exit → rebuild … which spins CPU, hammers the DB with reconnect attempts, and — on the shared tokio runtime — starves every other worker. That's how a brief DB blip cascaded into the fleet-wide stall that never recovered on its own.Fix
Prepend an async backoff to the cron feed on restart (
attempt > 0): 2s, 4s, 8s … capped at 60s. The delay stream yields no ticks, so no DB push is attempted until it elapses — turning the hot loop into exponential backoff. The worker now quietly retries until the DB recovers and resumes on its own, without a process restart.The bronze public-history queue workers already self-recover (the poll fetcher backs off internally, 1s→5min); only the cron pipe lacked this.
Where this sits (incident series)
#1157— cut the recurring heavy query that pressures the DB (fewer crashes).#1155— pool hardening: smaller connection footprint, survive resets.#1154(merged) — liveness self-heal: last-resort process restart + Sentry alert if a stall persists anyway.Testing
cargo fmt --check,cargo clippy --lib --bins -- -D warnings: clean. Unit test for the backoff curve (0 on first start; 2s→60s cap on retries).🤖 Generated with Claude Code
https://claude.ai/code/session_01FRWEKUFYCcmGGhwdDYd471