How to keep db updates in a queued job if timeout occurs? #57772
Replies: 3 comments 20 replies
-
|
Are you dispatching the jobs via Bus::batch()? |
Beta Was this translation helpful? Give feedback.
-
|
This is a classic transactional + long-running job conflict — and your intuition is correct: If your queued job (or its handle() method) runs inside a database transaction (implicitly or explicitly), and that job fails or times out: Laravel will rollback all pending changes. So any $user->updateReceived() calls will not persist, even though the emails were sent. In Laravel, this often happens because the queue worker wraps jobs in a transaction, depending on configuration. queue connection configuration (config/queue.php) You can disable transactional behavior for this specific job by adding: class SendBulkMailJob implements ShouldQueue } // Default connection (maybe transactional) |
Beta Was this translation helpful? Give feedback.
-
|
@victor-david use a child function of WithoutOverlaping as middleware and in it override the handle method: public function handle($job, $next)
{
$lock = Container::getInstance()->make(Cache::class)->lock(
$this->getLockKey($job),
$this->expiresAfter
);
if ($lock->get()) {
$lockWrapper = new \stdClass();
$lockWrapper->lock = $lock;
try {
\register_shutdown_function(function () use ($lockWrapper): void {
if (\isset($lockWrapper->lock)) {
$lockWrapper->lock->release();
}
});
$next($job);
} finally {
$lock->release();
$lockWrapper->lock = null;
}
} elseif (!is_null($this->releaseAfter)) {
$job->release($this->releaseAfter);
}
}Run your queue worker with limited amount of jobs to be processed in a PID to avoid the shutdown functions piling up and eating memory. php artisan queue:work --max-jobs=50 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello All,
Using Laravel 12.15.0, PHP 8.4.14
I have a queued job that sends the same email to a number of users. If the job runs to completion, it may be run again later to send to new users. Pseudo code is like this:
It's a bit more complicated than that, but that's the gist. User gets sent an email and the db is updated to show we sent them that one.
Let's say (for instance), after 100 users, the job times out. Then 100 users get an email, but (because of the timeout) all the updates that indicate who got the email are discarded in a rollback. Therefore, if the job is run again (whether manually or due to Laravel retry settings), those 100 users get a duplicate.
I know I can increase timeout, but the underlying issue is the same IF a timeout occurs.
How can I keep those updates? I've tried wrapping in my own transaction, tried try / catch, but all my tests turn out the same: the updates are discarded.
I think I must be missing something simple, but I don't know what it is. Any help much appreciated.
Beta Was this translation helpful? Give feedback.
All reactions