-
Notifications
You must be signed in to change notification settings - Fork 11k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Job-based retryAfter() method is called just once thus doesn't allow different values based on number of attempts #30509
Comments
Hey there, Unfortunately we don't support this version anymore. Please check out our support policy on which versions we are currently supporting. Can you please try to upgrade to the latest version and see if your problem persists? We'll help you out and re-open this issue if so. Thanks! |
Issue still persists. (latest version) One would expect when defining this method, the outcome of this method can be flexible. When the worker only call's this method once (in the beginning of building the queued message), the delay is almost as static as setting delay attribute on the command or with the delay method on the job. When the worker call's this method every time a jobs fails ( note: not marked as failed) or before the next try, then we could influence the delay-time (when the job will be available for retry). This will result in a sort off BackoffStrategy when needed. |
After some research..... The
As mentioned in my previous post the
As @bdenizar pointed out, there is an event dispatched when a job is failed. Source This solution is based on @bdenizar comment. The difference is that Backoff is defined in the Jobs. When the method This will result in an situation where different Jobs can live inside the same Queue with a different behavior (note: backoff / Delay). // app/Providers/EventServiceProvider.php
...
protected $listen = [
JobExceptionOccurred::class => [
BackoffFailedJob::class,
],
];
... // app/Listeners/BackoffFailedJob.php
<?php
namespace App\Listeners;
use DateTimeInterface;
use Illuminate\Contracts\Queue\Job;
use Illuminate\Queue\Events\JobExceptionOccurred;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\InteractsWithTime;
class BackoffFailedJob
{
use InteractsWithTime;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JobExceptionOccurred $event
* @return void
*/
public function handle(JobExceptionOccurred $event)
{
$job = $event->job;
if ($job->isDeleted() || $job->isReleased() || $job->hasFailed()) {
return;
}
if (! $backoffDelay = $this->getBackoffDelay($job)) {
return;
}
$job->release($backoffDelay);
}
/**
* Get the backoff delay in seconds from the job's command
*
* @param $command
* @return int|void
*/
private function getBackoffDelay(Job $job)
{
if (! $command = $this->getCommand($job)) {
return;
}
if (! method_exists($command, 'retryDelay')) {
return;
}
if (! $delay = $command->retryDelay()) {
return;
};
return $delay instanceof DateTimeInterface ? $this->secondsUntil($delay) : $delay;
}
/**
* Get the Command within the current job
*
* @param Job $job
* @return mixed
*/
private function getCommand(Job $job)
{
$command = unserialize($job->payload()['data']['command']);
if (in_array(InteractsWithQueue::class, class_uses_recursive($command))) {
$command->setJob($job);
}
return $command;
}
} // app/Jobs/TestJob.php
<?php
namespace App\Jobs;
use App\Jobs\Middleware\DelayJobMiddleware;
use App\Temperature;
use DateTimeInterface;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @param Temperature $temperature
*/
public function __construct()
{
$this->delay(10); // Or define a method retryAfter()
}
/**
* Execute the job.
*
* @return void
* @throws Exception
*/
public function handle()
{
throw new Exception(sprintf('Job %s failed.', Str::kebab(class_basename(self::class))));
}
/**
* When Failed
*
* @param Exception|null $exception
*/
public function failed(?Exception $exception = null)
{
//
}
/**
* Can returns an delay
*
* Used when the App\Listeners\BackoffFailedJob listener is registered.
*
* @return DateTimeInterface|int|void
*/
public function retryDelay()
{
if (! $delay = $this->delay ?? $this->jobDelay() ?? null) {
return;
}
return $this->calculateDelay($delay);
}
/**
* Get the delay of de Job
*
* @return int|null
*/
private function jobDelay()
{
return $this->job ? $this->job->payload()['delay'] : null;
}
/**
* @param int $delay
* @return int
*/
private function calculateDelay(int $delay = 0): int
{
return $delay * $this->attempts();
}
} |
Description:
This issue is related to this feature:
laravel/ideas#537
#28265
If you implement a job-class with a custom
retryAfter()
method then it will be called just once when the job is created and for all further attempts same delay value will be used.Steps To Reproduce:
Just create a
app/Jobs/TestJob.php
class with the following contents:Set
database
as the queue driver and start the queue.Expected: first time the delay should be 10 seconds, in all further tries - 60 seconds.
Actual result: it always applies 10 seconds delay.
The text was updated successfully, but these errors were encountered: