Skip to content

Store failed jobs from the Lambda queue handler only - #215

Draft
mnapoli wants to merge 2 commits into
masterfrom
record-failed-jobs-in-queue-handler
Draft

mnapoli wants to merge 2 commits into
masterfrom
record-failed-jobs-in-queue-handler

Conversation

@mnapoli

@mnapoli mnapoli commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Follows #214 (merged).

Problem

BrefServiceProvider registers a JobFailed listener that stores failed jobs with the failed job provider. It registers this listener in every process: the Lambda queue worker, but also the web functions, and php artisan queue:work / queue:listen / Horizon on a developer's machine.

php artisan queue:work already stores failed jobs itself (WorkCommand::logFailedJob(), also a JobFailed listener). So in local development, each failed job is stored twice. With Laravel's default database-uuids driver, the second insert fails on the unique uuid column, and that error replaces the job's own exception (see the previous PR for why). This is php artisan queue:work in a Laravel 13 application with this package installed:

local.INFO: Processing job 1 {"name":"App\\Jobs\\FailingJob"}
  App\Jobs\FailingJob .. RUNNING
local.INFO: FailingJob local: step 1
  App\Jobs\FailingJob .. 2.72ms FAIL
local.ERROR: SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: failed_jobs.uuid (Connection: sqlite, ..., SQL: insert into "failed_jobs" ...)

The job's exception is never logged. With the database driver (no unique uuid), the job is stored twice instead.

Two smaller issues come from the same listener:

  • It is registered in enableDetailedJobLogging(), so with bref.log_jobs set to false, failed jobs are not stored at all on Lambda. That option is documented as "disable detailed logging of every job execution".
  • It also stores failed sync jobs (SyncQueue calls Job::fail() too). A standard Laravel application does not store them: the exception is thrown to the code that dispatched the job.

Fix

QueueHandler, the Lambda equivalent of queue:work, now stores failed jobs, the same way WorkCommand does: it registers the JobFailed listener when it is created. The listener keeps the behavior of the previous PR: if the failed job cannot be stored, it reports that error and does not throw it.

Behavior changes:

  • php artisan queue:work / queue:listen / Horizon: each failed job is stored once, by Laravel.
  • Lambda: failed jobs are stored, whatever the value of bref.log_jobs.
  • Failed sync jobs are no longer stored, like in a standard Laravel application. (On the queue worker function, a sync job dispatched from inside a queued job is still stored, as with queue:work.)
  • The failed job provider is resolved only when a job fails, not on every boot of the application (including HTTP requests).

Tests

  • QueueHandlerTest::testTheFailedJobIsStored: the Lambda handler stores the failed job with the right connection, queue URL and exception.
  • QueueWorkCommandTest: php artisan queue:work with the database-uuids driver stores the failed job once, and only the job's exception is reported. This test fails before the change with the UNIQUE constraint failed: failed_jobs.uuid error.

Tested with Laravel 10, 11, 12 and 13, and on Lambda: a job with $tries = 1 that throws has the same logs as with the previous PR, and its SQS message is deleted.

The listener that stored failed jobs was registered by the service provider
in every process. `php artisan queue:work` stores failed jobs itself, so with
it (e.g. in local development) each failed job was stored twice: with the
default `database-uuids` driver, the second insert failed on the unique `uuid`
column and that error replaced the job's own exception.

The Lambda queue handler now stores failed jobs, like `queue:work` does. This
also means that failed jobs are stored even when `bref.log_jobs` is disabled,
and that failed `sync` jobs are no longer stored, like in Laravel. The failed
job provider is no longer resolved on every boot.

Claude-Session: https://claude.ai/code/session_01Awk33iZ9mDSg1xgSAPkgGK
…-jobs-in-queue-handler

# Conflicts:
#	CHANGELOG.md
Base automatically changed from log-job-exception-when-failer-fails to master September 27, 2026 08:42
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