Skip to content

fix(queue): prune the failed_jobs table on a schedule - #138

Merged
escapeboy merged 1 commit into
developfrom
fix/prune-failed-jobs
Aug 4, 2026
Merged

fix(queue): prune the failed_jobs table on a schedule#138
escapeboy merged 1 commit into
developfrom
fix/prune-failed-jobs

Conversation

@escapeboy

Copy link
Copy Markdown
Owner

Finding

failed_jobs on production holds 1916 rows, oldest from 2026-04, and had never been pruned — there is no queue:prune-failed anywhere in the scheduler.

This is not cosmetic. queue:failed prints the tail, so the table reads as "a few old entries from April" when it is actually nearly two thousand rows spanning four months. That is exactly how the recent, still-relevant failures get missed.

Change

Schedule::command('queue:prune-failed --hours=720')->dailyAt('04:15');

30 days is long enough to investigate a failure after the fact. Verified the entry registers on the container: 15 4 * * * php artisan queue:prune-failed --hours=720 — Next Due: 19 hours. The first run prunes the backlog; no manual deletion of production rows needed.

What the failures actually were (validated, not assumed)

17 failures since 2026-07-24:

Job Count Last Status
DistillTeamEventsJob (AiAccessUnavailableException + a 401 x-api-key header is required) 15 2026-07-29 fixed by #131 (2026-08-01)
RunPlanningStage (AI access, then MaxAttempts) 2 2026-07-28 same root cause
BuildArtifactJob (tool call validation failed … /max_steps) 1 2026-08-04 fixed by #136

The distill job ran nightly at 01:31 and failed every night from 2026-06-25 to 2026-07-29, then stopped.

"Stopped failing" was checked against "stopped running": memory:distill-events is still registered (30 1 * * *, next due in 16h), so the six clean nights since are the fix working, not a job that quietly fell out of the schedule.

Older classes in the table follow the same shape — each stops at the date its fix shipped (e.g. 39 × ExperimentTransitioned | BroadcastException ending 2026-06-29).

Verification

pint + phpstan clean; schedule entry confirmed live on the production container.

failed_jobs had never been pruned and had grown to 1916 rows on production,
the oldest from 2026-04. Every row is a resolved incident, and the volume
drowns the handful that still matter — `queue:failed` shows only the tail, so
the table reads as "3 old entries" when it is nearly two thousand.

`queue:prune-failed --hours=720` keeps 30 days, which is long enough to
investigate a failure after the fact. Verified the schedule entry registers
(`15 4 * * *`, next due 19h).

Found while validating the current failures rather than assuming they were
stale: of the 17 since 2026-07-24, all but one are DistillTeamEventsJob at
01:31 nightly, which stopped after 2026-07-29 (fix #131, 2026-08-01 — six
clean nights since, with `memory:distill-events` confirmed still scheduled and
due, so the silence is a fix and not a job that quietly stopped running). The
remaining one is the browser_task max_steps 400, fixed in #136.
@escapeboy
escapeboy merged commit e0c5e9a into develop Aug 4, 2026
3 checks passed
@escapeboy
escapeboy deleted the fix/prune-failed-jobs branch August 4, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant