framework: Laravel 5.3 Job fails without calling failed() method
In Laravel 5.3, a Job will silently fail (no method invocation and nothing in the Laravel log) if you have not updated the jobs failed() method to receive an Exception.
For example, the following code is acceptable in Laravel 5.2, but will not be called in Laravel 5.3.
public function failed()
{
// handle failure
}
It needs to be updated to:
public function failed(Exception $exception)
{
// handle failure
}
While I would not consider the update a bug, it was rather time consuming to track down since no error was reported. After determining the error, part of me thinks the unexpected method signature should throw an exception.
If nothing else, this was not listed in the Upgrade Guide, so opening this issue for future readers.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 56 (34 by maintainers)
I seriously would never use the DB driver for queues in production. It is no intended to be a production solution.
We’re doing an upgrade to 5.3 right now and coming across some weirdness around failed jobs with the database queue driver.
If I have an email job sitting in
jobstable, and throw an intentional exception, then runphp artisan queue:listen --tries=3, I expect the job to fail 3 times then move to thefailed_jobstable (at least that’s how it worked for us in 5.2). However in 5.3, the job disappears off both tables with no valuable output by the listener, and no email sent.So confused! Not sure if our problems are related, but it seems something is funky with failed job handling and database queues right now.
I had same issue that job wasn’t written into failed_jobs table after it falied. I finally found this error in log:
I had to delete migration file and entry in migrations table for failed_jobs and rerun php artisan queue:failed-table command. This happened after I upgraded from 5.2 to 5.3. I didn’t find anything in documentation about this change and need to update structure of failed_jobs table.
Hope this helps at solving the issue.
Was jobs table structure changed as well?
2016_08_08_115250_create_failed_jobs_table.php.txt 2016_10_05_145707_create_failed_jobs_table.php.txt
@taylorotwell I work with @JesseLeite and I have been able to reproduce a use case where jobs will not make it to the failed jobs table.
Use Case
Illuminate\Queue\Worker@processIlluminate\Queue\Jobs\Job@fireIlluminate\Queue\Worker@handleJobExceptionIlluminate\Queue\Worker@markJobAsFailedIfHasExceededMaxAttemptsIlluminate\Queue\Worker@failJobjobstable.Illuminate\Queue\Jobs\Job@failedIlluminate\Queue\Worker@failJob$this->raiseFailedJobEvent($connectionName, $job, $e);does not get executed.raiseFailedJobEventmethod fires off aEvents\JobFailedevent, which gets picked up down the line byIlluminate\Queue\Failed\DatabaseFailedJobProvider.DatabaseFailedJobProvideris what is responsible for inserting the failed job into thefailed_jobstable.Summary
The problem as far as I can tell is that the
firemethod and thefailedmethod onIlluminate\Queue\Jobs\Jobare almost identical, so if an error is thrown in thefiremethod then it will surely be thrown in thefailedmethod as well.If there is something wrong with the payload then an exception could be thrown. For example a
ReflectionExceptionif a class does not exist or an exception is thrown when trying to unserialize the command. If something is wrong with the payload the job will never succeed, but we would still like to have it in thefailed_jobstable, so that we can analyze the issue.In our case we were overriding functionality in
Illuminate\Queue\SerializesModels@__wakeupthat was throwing an error when unserializing the command inIlluminate\Queue\CallQueuedHandler@call.Since an error gets thrown in the
firemethod thefailedmethod also throws an exception. The job is deleted and the event that inserts the record in thefailed_jobstable is never called.How to Replicate
The following can be replicated on a fresh install of Laravel:
.envQUEUE_DRIVER=databasephp artisan queue:tablephp artisan queue:failed-tablephp artisan migratephp artisan queue:listen --tries=1jobstable and not inserted into thefailed_jobstable.ReflectionExceptionis thrown when it tries to make the class from the payload.Honestly this is a bit frustrating. I’ve unknowingly been using Queues with the database driver since Laravel 5.1. It’s worked fine until 5.3. Now, from my perspective, it doesn’t work in 5.3, so I open an issue and the answer is basically - you shouldn’t have been using that.
Fine, but let’s at least document that if there are no plans to make the database driver production-ready. I doubt I’m the only person using the database driver in production. Clearly, there’s at least 4 of us 😉
After hours of tests and research, I just removed the “Exception” word from the function and it worked for me. Laravel 5.4.
Command “php artisan queue:work --tries=1”
Before (not working):
After (working)
Could this be a bug?
@themsaid reading through this issue thread again, it seems everyone that has reported issues here was using the database driver (references to
jobsandfailed_jobstables, or explicitly stating they are using thedatabasedriver).Currently my job is failing because of the issue @jasonmccreary raised in the first place
I can find this in my
failed_jobstable in theexceptioncolumn.Making sure that the
failedmethod gets the correct exception as an argument (see below) I see my jobs disappearing after failing. So they can’t be found in thejobsor thefailed_jobstable. There is also no exception in the log…@GrahamCampbell The
failedmethod inInteractsWithQueueis currently not matching the interface the given Job has.Just to keep this up to date, the Database driver is now acceptable for production use cases alongside MySQL 8.0: https://divinglaravel.com/a-production-ready-database-queue-diver-for-laravel
Give me a fresh application that recreates that problem.
To be fair, if failed jobs are unexpectedly lost to the ether, it’s not stable enough for any environment 😛