The problem:
I have a job in Laravel, based on the condition that an API is reachable it should either run or be released a day later.
The condition to check if the API is reachable works perfectly. The problem, however, occurs when the job is released again. I defined it as $this->release($dayInSeconds); where $dayInSeconds = 86400;. So, according to my understanding, the Job should be released to queue again, after 86400 seconds (a day).
The docs defines this behaviour here: Manually releasing a job, and this (old) answer also confirms that I understand the release() method correctly. Laravel 4.2 queues what does $job->release() do?.
However, when I call $this->release($dayInSeconds) the job is released again, ranging with a delay of 6 minutes to 4 hours. (We get notifications in a dedicated Teams channel when this happens). However, this should only happen after a day, not after 6 minutes or 4 hours.
The question:
Why is my Job not being released after a day, even though I think I have the correct understanding of the release() method? Am I missing something or somehow still understanding the release() method wrong?
Useful information:
Useful code snippets:
The Job:
class SendOrderTo<REDACTED> implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ConsoleBaseMethodsTrait;
private int $dayInSeconds = 86400;
public $tries = 5;
public $timeout = 60;
public $backoff = 300;
public $order;
public function __construct (Order $order)
{
$this->order = $order;
}
public function handle ()
{
if (!$this->isApiReachable()) {
// Re-schedule the job for a day later
$this->release($this->dayInSeconds);
// Notify on Teams Alert channel.
$orderId = $this->order->id;
$orderHostName = $this->order->host->name ?? NULL;
TeamsTrait::notifyOnTeams('<REDACTED> Job ' . $orderHostName . ' order ' . $orderId . ' has been re-scheduled.',
'Due to an outage in the <REDACTED> Service this job has been delayed by a day.');
}
// Other logic in the handle() that is not relevant for the question.
}
public function failed (Exception $e)
{
// Just some logging, also not relevant.
}
private function isApiReachable () : bool
{
$data = getServicesAvailabilityFile();
return $data->services->api ?? false;
}
Clarifications:
I used REDACTED in some spaces, this means I am unable to publicly show this name, should not impact the question.
$data in the isApiReachable() method is a JSON file, looks something like this, it returns either true or false:
{"services":{"api":true,"other":true,}}
If I understood correctly, you want to use the queue more like a cron runner, scheduling tasks over long periods of time.
These queues are more intended to respond to messages fairly quickly. Hence it's not really built for very long release timeouts. The values in Laravel documentation are more like 5 or 10 seconds. It's more intended to do some short term delays intended to keep the queue functioning properly.
If you want to use it with longer timeouts, it will probably still work, after modifying any other timeouts that release the job earlier in your application configuration.
The jobs are ultimately selected on the available_at field, it should be the right date calculated from your $delay. See DatabaseQueue. Does your job's database record have the expected date in that field? If it does, it means something else is releasing the job early.
/**
* Modify the query to check for available jobs.
*
* @param \Illuminate\Database\Query\Builder $query
* @return void
*/
protected function isAvailable($query)
{
$query->where(function ($query) {
$query->whereNull('reserved_at')
->where('available_at', '<=', $this->currentTime());
});
}
Laravel also comes with a task scheduling component. If you schedule it to run only once a day, you don't even need to make the job unique.
This seems like a better choice if you need cron like behavior.
Another thing you could try in your existing code, as you already have a unique identifier ($this->order->id), is "unique jobs". You can specify how long it needs to be unique, and that has a 1 hour value in the example. Probably it will work for a day too, but I'm not sure if it would affect if the job itself is released.
Can you give more detail about the use case? Why is the delay one day? It's hard to imagine a case where this would work well. You don't need that much bad luck to delay it by multiple days. If you happen to be executing always at a bad time of day, the order might never complete. Hence using a CRON schedule helps you control the timings better.