Reputation: 2413
so in laravel I have a function that posts some content and pushes a job onto a queue and returns some data from the function(not the queue).
Now I thought that laravel queues were supposed to be some sort of async function that makes sure your code remains quick even though there is a lot of data to handle so you put it in a job. But I noticed that my code still needs to wait till the job is finished before it returns data to the user. Here's a log of the script called 2 times but one is with almost no data in the job, and the other has a lot:
[2016-09-08 13:26:50] production.INFO: New alert with Image
[2016-09-08 13:26:50] production.INFO: Push is send.
[2016-09-08 13:26:50] production.INFO: Alert data is send back.
[2016-09-08 13:26:50] production.INFO: New image is valid
[2016-09-08 13:26:50] production.INFO: Move file for upload
[2016-09-08 13:26:50] production.INFO: Made it to upload
[2016-09-08 13:28:50] production.INFO: New alert with Image
[2016-09-08 13:31:19] production.INFO: Push is send.
[2016-09-08 13:31:19] production.INFO: Alert data is send back.
[2016-09-08 13:31:20] production.INFO: New image is valid
[2016-09-08 13:31:20] production.INFO: Move file for upload
[2016-09-08 13:31:20] production.INFO: Made it to upload
As you can see the second ones takes 4 minutes before the application receives further data. This is a deal breaker you can't make users wait so long. So how do I get the job to run async and the function doesn't have to wait for it to finish.
Here's the line where I call the code:
if($isValid === true) {
$this->dispatch(new SendPushNotificationAlert($alert));
Log::info('Push is send.');
}
And here is my job:
<?php
namespace App\Jobs;
use DB;
use Log;
use App\Alerts;
use App\Users;
use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Http\Controllers\PushNotificationsController as PushNotificationsController;
class SendPushNotificationAlert extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
Protected $alert;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Alerts $alert)
{
//
$this->alert = $alert;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$alert = $this->alert;
$radius = 10000;
$earthRadius = 6371000; // earth's mean radius, m
// first-cut bounding box (in degrees)
$maxLat = $alert->lat + rad2deg($radius/$earthRadius);
$minLat = $alert->lat - rad2deg($radius/$earthRadius);
// compensate for degrees longitude getting smaller with increasing latitude
$maxLon = $alert->lon + rad2deg($radius/$earthRadius/cos(deg2rad($alert->lat)));
$minLon = $alert->lon - rad2deg($radius/$earthRadius/cos(deg2rad($alert->lat)));
$locations = DB::select("
SELECT id, lat, lon, user_id, name, radius,
acos(
sin(?)*
sin(radians(lat))+
cos(?)*
cos(radians(lat))*
cos(radians(lon) - ?)
) * ? AS distance
FROM (
SELECT id, lat, lon, user_id, name, radius
FROM user__locations
WHERE lat BETWEEN ? AND ?
AND lon BETWEEN ? AND ? AND hide = 0
) AS FirstCut
WHERE acos(
sin(?)*
sin(radians(lat))+
cos(?)*
cos(radians(lat)
)*
cos(radians(lon) - ?)
) * ? < ?
ORDER BY distance", [
deg2rad($alert->lat),
deg2rad($alert->lat),
deg2rad($alert->lon),
$earthRadius,
$minLat,
$maxLat,
$minLon,
$maxLon,
deg2rad($alert->lat),
deg2rad($alert->lat),
deg2rad($alert->lon),
$earthRadius,
$radius
]);
if(count($locations > 0)) {
foreach ($locations as $location) {
if($location->distance < $location->radius) {
if($alert->anoniem == 0) {
$user = Users::find($alert->user_id);
$pushuser = Users::find($location->user_id);
$pushuser->type = 'location';
$pushuser->locationname = $location->name;
$pushusers[] = $pushuser;
}
}
}
}
if(isset($pushusers)) {
PushNotificationsController::sendPushnotificationsAlert($pushusers, $user);
}
}
}
Anyone knows why the job doesn't run async and what I could do to fix it?
Upvotes: 9
Views: 15311
Reputation: 8419
For laravel >= 6.14, you can use
$array_or_object = [];
YourJobName::dispatchAfterResponse($array_or_object);
Ref: https://laravel.com/docs/8.x/queues#dispatching-after-the-response-is-sent-to-browser
To have properly structured job you need
php artisan make:job YourJobName
Ref: https://laravel.com/docs/8.x/queues#generating-job-classes
Example
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
class YourJobName implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
protected $received_object;
public function __construct($received_data)
{
$this->received_object = $received_data;
}
public function handle()
{
//do_some_thing($this->received_object);
}
}
Find it explained in easy wording: https://divinglaravel.com/running-a-task-after-the-response-is-sent
Upvotes: 2
Reputation: 12286
I have been getting the same issue in Laravel 5.7. By default, Laravel queue is configured to run synchronously. Open config/queue.php
, switch to anything else other than sync
For example:
'default' => env('QUEUE_CONNECTION', 'redis'),
Alternatively, you can also add this to your .env file.
QUEUE_CONNECTION=redis
Upvotes: 7
Reputation: 149
What queue driver do you use?
As described here: https://laravel.com/docs/5.2/queues#introduction you have multiple options but if you use synchronous
(for local environments only) it'll not run Async.
And does your Job implement the Illuminate\Contracts\Queue\ShouldQueue
?
As described here: https://laravel.com/docs/5.2/queues#writing-job-classes you'll see that if you don't implement it the job will run synchronously.
Hope it'll help!
Saw you already implement the Illuminate\Contracts\Queue\ShouldQueue
, so that could not be the problem.
Upvotes: 1