belzaaron liked a comment+100 XP
1mo ago
I have a Laravel app using AWS SQS for queued jobs. We are running on Laravel Vapor but that doesn't really matter for this except that it doesn't support deferred work (work after the response is sent). The latency to add a job to SQS can range from 50-150ms. If one web request is queuing a lot of jobs up (notifications, syncing data to third party services, etc.) the latency can add up and slow down the response pretty quickly.
I came up with the idea of using a "Queue Bucket" that collects all jobs dispatched in a request as closures and right before the response is sent, creates one job that then handles dispatching all the other jobs. I am using middleware to call QueueBucket->run() that is attached to all web requests so I don't forget to call it somewhere.
Any thoughts on this? Is there a better way?
One caveat is that it doubles the model hydration queries as models are re-hydrated when QueueBucketJob runs and within each job that it queues up too. But I'll take that over slowing down a request by a second or more.
Here is the code. Using SerializableClosure came from Aaron Francis' recent YT video.
QueueBucket.php
<?php
namespace App\Domain\Shared\Jobs;
use Closure;
use Illuminate\Support\Collection;
use Laravel\SerializableClosure\SerializableClosure;
class QueueBucket
{
/**
* @var Collection<int, Closure>
*/
private Collection $jobs;
public function __construct()
{
$this->jobs = new Collection();
}
public function add(Closure $job): void
{
$this->jobs->push($job);
}
public function run(): bool
{
$shouldDispatch = $this->jobs->isNotEmpty();
if ($shouldDispatch) {
dispatch(
new QueueBucketJob(
$this->jobs->map(fn (Closure $job) => new SerializableClosure($job))
)
);
$this->jobs = new Collection();
}
return $shouldDispatch;
}
}
QueueBucketJob.php
<?php
namespace App\Domain\Shared\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Collection;
use Laravel\SerializableClosure\SerializableClosure;
class QueueBucketJob implements ShouldQueue
{
use Queueable;
/**
* @param Collection<int, SerializableClosure> $jobs
*/
public function __construct(
private Collection $jobs
) {}
public function handle(): void
{
$this->jobs->each(fn (SerializableClosure $job) => $job->getClosure()());
}
}
Examples of adding jobs/queuable work to the bucket
I am actually using a Facade for QueueBucket so that is why the methods are being called statically. QueueBucket is also instantiated in the Service Container as a scoped instance (a single instance per request).
QueueBucket::add(fn () => SendNewUserAdminNotification::send($user));
QueueBucket::add(fn () => App::make(UpdateEmailListSubscriberAction::class)->onQueue()->execute($user));
QueueBucket::add(fn () => $user->notify(
new AccountEmailVerificationNotification(
$emailRecord->new_email,
$emailRecord->verification_code
)
));
belzaaron liked a comment+100 XP
3mos ago
This LaryAI stuff again... Just another AI junk. Anyone can ask claude or something and get some not exaclty right answer like this one. It's really annoying that people put this trash everywhere. Such a shame this useless sh...t is also here.