Laravel queue randomly not dispatching - php

I'm trying to dispatch jobs from a controller, but sometimes the queue works, and sometimes it will execute the code immediately. On the other hand, dispatching from a command works fine, so I wonder what the difference could be.
Things that I have tried:
I tried database and Redis connections and got the problem for both connections
Without running queue listen/work, the job will randomly execute, so listen/work cannot be the problem
Checked webserver/PHP log for errors, no result
Something goes wrong before the static dispatch function. When I put a die in the static dispatch function, it will sometimes kill the app, and sometimes it will still run decode in the handle immediately....
vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php
public static function dispatch()
{
die('xxx');
return new PendingDispatch(new static(...func_get_args()));
}
I'm trying to find what happened before the dispatch function, but I cannot find any function that executes before the static handle function. I have the feeling that something is crashing in PHP and alternatively execute the job immediately, but I cannot find where that happens in the core.
Controller
class CreateTestJob
{
public function __invoke(Request $request)
{
TestJob::dispatch(rand(0, 999999));
}
}
Job
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct($test)
{
$this->test = $test;
}
public function handle()
{
sleep(30);
echo 'done';
}
}
I also tried to create a syntax error in de handle function of the TestJob so that I can see the stack trace, but it shows "1 unknown frame". I wonder what that is

you can check fail_jobs table if there is any error while running your job.
The job will be immediately executed only when you use synchronous driver or synchronous dispatch(for use in local development).
<?php Somejob::dispatchSync();
Jobs on Redis and database connection will be executed only when you run queue worker.
php artisan queue:work

Related

Laravel queue job with SerializesModels trait retrieves outdated model data

I'm using the Laravel queue through redis to send notifications. But whenever I pass models to the notifications, their properties are outdated when the notification is sent through the queue.
This is basically what I'm doing:
In controller (or somehwere else):
$thing = new Thing(); // Thing is a model in this case.
$thing->name = 'Whatever';
$thing->save();
request()->user()->notify(new SomethingHappened($thing))
SomethingHappened.php:
<?php
namespace App\Notifications;
use App\Thing;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class SomethingHappened extends Notification implements ShouldQueue
{
use Queueable;
public $thing;
public function __construct(Thing $thing)
{
$this->thing = $thing;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
dump($this->thing->name); // null
return (new MailMessage())
// ...
// ...
// ...
;
}
}
Now when I add a delay of a few seconds (i.e. (new SomethingHappened($thing))->delay(now()->addSeconds(5))), the retrieved model is up-to-date. Also, before I deployed enough queue workers (and they were lagging behind on a filling queue), this issue didn't exist. Therefore, it appears now that when queue job gets processed really quickly, it doesn't retrieve the model from the database correctly or the model isn't saved yet. I have no idea why this could be happening, since the save call on the model is clearly executed (synchronously) before dispatching the notification, so there should be no way it isn't saved yet when the job is processed.
I'm running on Kubernetes and using a MySQL database. Everything is on GCP (if this is relevant).
Does anyone have an idea? Adding a delay of a few seconds is not a great solution and shouldn't be necessary.
It appears our database was using too much CPU, causing it to switch to the failover regularly. I think this was the cause of the issue. After implementing tweaks reducing the database load, the problem was resolved. A workaround for this issue would be to add a slight delay (as noted in my question).
I'm not sure why this only seemed to affect notifications, and not other queue jobs or requests. If anyone has a more detailed explanation, feel free to post a new answer.

Queueing mails in Laravel

I would like to send mail to user after creating account on my website and I would like to use queues to send them. I'm using PHP Laravel framework.
My controller handles the request after clicking on "Create account":
class LoginController extends Controller
{
...
public function register(Request $request) {
...
$mail = (new RegisterRequest($user))->onConnection("database")->onQueue("emailsQueue");
Mail::queue($mail);
...
}
}
Then I have this RegisterRequest (mailable) class:
class RegisterRequest extends Mailable
{
use Queueable, SerializesModels;
protected $user;
public function __construct($user)
{
$this->user = $user;
}
public function build()
{
return $this->from('user#example.com')
->to($this->user->email)
->subject("Confirm your Email Address")
->view('emails.register.request')
->with("registration_token", $this->user->registration_token);
}
}
As you can see, I am using relational database to store jobs. And really, after calling LoginController's register method, a job is saved to database. But it can't be processed. I also start php artisan queue:work but nothing is done with jobs in database. Any help?
EDIT:
So I just found out that picking jobs from queue is done by SQL selecting the 'default' queue name. But I'm sending mails to queue 'emailsQueue'. So I'm now running Queue Worker like this: php artisan queue:work --queue=emailsQueue and everything's working fine for now. But how can I pick jobs from every queue in database? It's probably not the best attempt, right? It wouldn't make any sense to have named queues, right? But let's say I have one queue for processing register account requests, another queue for changing password requests and so on... So I think it does make sense to process every queue. So how can I do this? Can it be done just by listing the queues like this?
php artisan queue:work --queue=registerAccountEmailQueue,changePasswordEmailQueue...
What exactly does running php artisan queue:work? I thought it's the command to run all queues.
Use queue driver database.
In controller you should write
$this->dispatch(new SendNotifyMail($data));
This will pass your $data to queue. here SendNotifyMail is used as Job Class. So you should also use this in Controller like use App\Jobs\SendNotifyMail;.
Then create a file in Folder Jobs, named SendNotifyMail
<?php
namespace App\Jobs;
use App\Jobs\Job;
use DB;
use Mail;
use Artisan;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendNotifyMail extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
public $timeout = 300; // default is 60sec. You may overwrite like this
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function handle(Mailer $mailer)
{
$data = $this->data; // retrieve your passed data to variable
// your mail code here
}
}
In your command you need to write
php artisan queue:listen
or
php artisan queue:work
Then execute the code.

Laravel / PHP - Run 3 php functions in parallel

I have 3 functions that get json data from external apis and then saves in my database. Each function is its in own class e.g :
Class api1 {
public function fetch()
{
//Do Something
}
}
Class api2 {
public function fetch()
{
//Do Something
}
}
Since its api call might take some time or delay . I want to run all 3 in parallel so that api2 does not have to wait for api1 to complete.
Any way to do that ?
* Note : I'm also going to use laravel scheduler which will run each function every minute or run a single function containing all 3.
To me this looks more of like callback request for data, so to keep your app from not slowing down this should be a background job.
But before that I would implement an interface for those classes:
interface apiService{
public function fetch();
}
Class api1 implements apiService {
public function fetch()
{
//Do Something
}
}
Class api2 implements apiService{
public function fetch()
{
//Do Something
}
}
Create a job class php artisan make:job dataFetcher
Jobs will be structured under App\Jobs\
The job class in Laravel its dead simple, consisting of a constructor to Inject dependencies and handle() to fire the job.
protected $service;
public function __construct(apiService $service)
{
$this->service = $service;
}
public function handle()
{
$this->apiService->fetch();
}
Note that I am injecting the interface instead of concrete class, using a bit more high level code here. So now you can create a command to fire the calls with a cron job, or you can create a custom service provider to fire the commands as soon as app bootstraps.
I would go with a custom artisan command here:
So just create a custom artisan command on handle method
public function handle()
{
Job::dispatch(new FirstApiClass);
Job::dispatch(new SecondApiClass);
}
Handle method will execute first line and Job will be processed in background(doesnt matter if job failed or not), then next call will be fired and so on...
Note the use of the interface in this case, Job class doesnt really care which service you are calling as long as you provide an implmenetation of it.

Laravel Job deserialzation wrong class

I've got a problem while using Laravel's job Deserialization.
This is the Job class that is queued in the database:
class SendRatingEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $order, $user;
public function __construct(User $user, Order $order)
{
$this->order = $order;
$this->user = $user;
}
public function handle()
{
if ($this->order->isRatedByUser($this->user->id)) {
return;
}
Mail::to($this->user->email)->queue(new RatingEmail($this->order, $this->user));
}
}
In the class Order.php, I dispatch this job like this:
class Order {
function queueRating()
{
$when = Carbon::now()->addDays(env('ORDER_DAYS_RATING', 8));
dispatch((new SendRatingEmail($this->buyer, $this))->delay($when));
}
}
So the problem is in the job's handle() function, specifically the error is:
Call to undefined method Illuminate\Database\Query\Builder::isRatedByUser()
It seems as though Laravel gives me the wrong object, instead of App\Order it gives me the QueryBuilder. In the queueRating() function I have checked that the types given in the constructor are the expected types. I have even tested a workaround which also didn't seem to work:
if($this->order instanceof \Illuminate\Database\Query\Builder) {
$this->order = $this->order->first();
}
Also I have looked in the jobs table, and it seems as if the saved models are correct (App\Order)
Edit
Here is the code where the queueRating() function is called. The file is StripeController which handles credit card payments.
public function orderPaid($order) {
$order->payment_done = 1;
$order->save();
$order->chat->open = 1;
$order->chat->save();
$order->queueRating();
}
I found the problem, as always the problem is not where I looked. As it turns out, the code is completely fine, but I forgot to restart the queue worker on the staging server, which meant that changes in the code base were not applied in the queue worker. So the application and queue used different versions.
It is even stated in the Laravel Queue Documentation:
Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers.
I use the bugtracker Bugsnag, which shows the error and also the line numbers in the code. I had noticed that the line number where the error occurred mismatched the real line number, but couldn't figure out that this was causing the problem.

Laravel job calling class from nowhere?

I have a job in my Laravel project (v.4.2). which is used for inserting data into database. The class named "ProductUpdate". I use Amazon SQS for queue service.
What makes me confuse now is, when I changed the code in class "ProductUpdate",
it seems that the job is running by using old version of the class.
I even deleted all lines of code in the class but the jobs can still be able to run ( it stills inserts data).
Following is the job class.
The file of this class is at app/jobs/ProductUpdate.php
In my understanding, job class is the only place that will be called from queue, but why it can still be able to run when I deleted all the codes?
<?php
/**
* Here is a class to run a queued item sent from SQS
* Default method to use is fire()
**/
class ProductUpdate
{
public function fire($job, $data)
{
// Disable query log
DB::connection()->disableQueryLog();
// Set the job as a var so it will be used across functions
$this->job = $job;
$product = Product::find($productID);
if($product->product_type != 18) {
// Call the updater from library
$updater = App::make('Product\PriceUpdater');
$updater->update($product);
}
// Done and delete
$this->success();
}
private function success()
{
$this->job->delete();
}
private function fail($messages = array())
{
Log::error('Job processing fail', $messages);
$this->job->delete();
}
}
Your problem is related to cache.
Run this command in terminal to remove all cached data.
php artisan cache:clear
other way:-
Illuminate\Cache\FileStore has the function flush, so you can also use it:
Cache::flush();
This link will also help you :)

Categories