Laravel - Retry failed jobs on a specific queue - laravel

I know that I can retry jobs that have failed in my Laravel application by using: php artisan queue:retry 5 OR php artisan queue:retry all to push them back onto the queue.
What I would like to achieve though is to only retry failed jobs from a single queue. Such as php artisan queue:retry all --queue=emails which does not work.
I could however go through each manually by ID php artisan queue:retry 5 but this does not help if I have 1000's of records.
So in summary, my question is, how can I retry all failed jobs on specific queue?

Maybe you can create another command
lets say
command : php artisan example:retry_queue emails
class RetryQueue extends Command
{
protected $signature = 'example:retry_queue {queue_name?}';
protected $description = 'Retry Queue';
public function __construct()
{
parent::__construct();
}
public function handle()
{
// if the optional argument is set, then find all with match the queue name
if ($this->argument('queue_name')) {
$queueList = FailedJobs::where('queue', $this->argument('queue_name'))->get();
foreach($queueList as $list) {
Artisan::call('queue:retry '.$list->id);
}
} else {
Artisan::call('queue:retry all');
}
}
}

Related

How can I run in background command, which runs only in foreground?

I have the command, which works like php artisan queue:listen. And it can't work in background in common, but I have to add it to cron tab, but it does not work there. Does it possible to do something like php artisan schedule:run ? The most imortant that when I interrupt this command, all functionalyty will stop. What do I have to do in this situation?
Laravel has his own cron. First of all, you should add Laravel cron to Linux system cron
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
then you can add your commands to Laravel cron.
Laravel crons lives inside a file /app/Console/Kernel.php
the are should be inside function
protected function schedule(Schedule $schedule)
for example
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:send Taylor --force')->cron('* * * * *');
}
But if you want your command run as a system process not as a cron you should use supervisors program(supervisord) or you can create for PHP command file a systemd service file and then run as if the are a normal systemd service and even manage this service through monit program in with web interface as well
If your php script is a process it means that the constructor method of class runs only ones when you start your script and if you red db data in the constructor that data in the script would be stale
Your process script should be process something like this
class OpenOrders extends Command
{
public function __construct()
{
parent::__construct();
}
public function handle()
{
$this->initTicker();
$this->updateBalances();
//the Process
while (true) {
//this is the right place to read DB data
$this->getAllOptions();
$this->openOrders = $this->getOpenOrders();
}
return 0;
}
}

Laravel: Check if new items added to db table and schedule a job to email user

I want to trigger an email when new rows are added to a table in my Laravel application. However I want to add a buffer of sorts, so if 5 rows are added in quick succession then only 1 email is sent.
The method I've chosen is to schedule a check every 15 minutes and see if there are new rows added. If there are then I will queue an email.
Currently I'm getting an error on the schedule. I'll run through my code below:
In Kernel.php where we setup schedules I have:
$schedule->job(new ProcessActivity)
->everyFifteenMinutes()
->when(function () {
return \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(15), Carbon::now()))->exists();
})
->onSuccess(function () {
Log::debug(
'Success'
);
})
->onFailure(function () {
Log::debug(
'Fail'
);
});
Which I use to trigger the Job found in: App\Jobs\ProcessActivity.php :
public function __construct()
{
$this->jobs = \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(15), Carbon::now()))->get();
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
Log::debug('Activity Job Run', ['jobs' => $this->jobs]);
$this->jobs->each(function ($item, $key) {
Log::debug('loop');
// get project
$project = $item->project;
// get project email
$user_id = $project->user_id;
$email = \App\User::find($user_id)->email;
// get project UUID
$projectUuid = $project->public_id;
// emails
$subscriberEmails = \App\ProjectSubscription::where('project_id', $project->id)->get();
// create activity email
Notification::route('mail', $subscriberEmails)->notify(new Activity($project, $projectUuid));
});
return true;
}
I've posted my full code above which also shows a relationship between my JobItems and Project models. I won't elaborate on that as I've commented in the code.
The problem
When I add a new row to my JobItem table I can see the job is scheduled and processed (using Laravel Telescope to inspect this).
However, I can also see in my log that for each job I get two log messages:
First: 'Fail' and then 'Activity Job Run'
My email is not sent and I'm uncertain how to determine why this is failing.
So it seems that onFailure is being triggered and there is a problem with my ProcessActivity.
Any clues on where I am going wrong and how to determine the error would be much appreciated.
I have a fix, but first, here are some things I learnt that hampered my progress:
I was using this artisan command to process my scheduled jobs:
php artisan queue:work
The problem with developing while using that command is that if there are code changes then those changes are not recognised.
So you can either Command+C to return to the console and use this every time there is a code change:
php artisan queue:restart
php artisan queue:work
Or you can just use this and it will allow code changes:
php artisan queue:listen
As you can imagine without knowing this you will have a slow debugging process!
As a result of this and adding an exception to my Job I made some progress. I'll paste in the code below to compare against the original code:
public function __construct()
{
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
try {
$jobs = \App\JobItem::whereBetween('created_at', array(Carbon::now()->subMinutes(20), Carbon::now()))->get();
Log::debug('Activity Job', ['jobs' => $jobs]);
// collection start
$collection = collect();
// loop jobs to get emails
foreach ($jobs as $key => $value) {
// get project UUID
$project = $value->project;
$projectUuid = $project->public_id;
// get emails subscribed to projects
$subscriberEmails = \App\ProjectSubscription::where('project_id', $project->id)->get();
// merge into a single collection via the loop
if ($key != 0) {
$merge = $collection->merge($subscriberEmails);
$collection = collect($merge);
} else {
$collection = $subscriberEmails;
}
// Log::debug('emails_project in loop', ['emails' => $subscriberEmails]);
};
// clean object with uniques only
$subscriberEmailsCleaned = $collection->unique();
// debug
Log::debug('Project Emails to Notify', ['emails' => $subscriberEmailsCleaned]);
// create activity email
Notification::route('mail', $subscriberEmailsCleaned)->notify(new Activity($project, $projectUuid));
} catch (\Exception $e) {
\Log::info($e->getMessage());
}
}
First thing to note, is that as __construct() is run initially and is serialised. Then the handle method is called when the job is processed. So I had to move my eloquent query into the handle method.
I also used a foreach rather than .each to loop through and create a new collection of emails. Perhaps there is a more elegant way, but I needed to create a collection of emails and this way allowed me to move the variables in the loop outside to be used in the method.
You can see me merge these at the bottom of the loop.
I have also added a few Log:: items which is useful for debugging.
Not fixed 100%
With this code I can now auto schedule an email every x minutes when new items are added. However, I am still getting the log Fail from the onFailure()from my Kernal.php file:
->onFailure(function () {
Log::debug(
'Fail'
);
I am still confused as to what that indicates and how I can determine more information about how this has failed and what that means. However, it does work so I will cautiously move forward (with one eye open on the comments, in case someone has an idea that can help!)

Laravel Undefined property in job

In a Laravel job I have:
use Spatie\Valuestore\Valuestore;
and
public function __construct()
{
$this->settings = Valuestore::make(storage_path('app/settings.json'));
}
and
public function handle()
{
if($this->settings->get('foo') == 'test') {
etc...
and on this I get an error Undefined property App\Jobs\MyJobName::$settings. What is going wrong?
Even if I do this:
public function handle()
{
$this->settings = Valuestore::make(storage_path('app/settings.json'));
if($this->settings->get('foo') == 'test') {
etc...
I get the same error.
Update based on the comments
MyJobName is called in a custom artisan command, that happens to also use Valuestore but I assume that would unrelated.
In the class CustomCommand:
use Spatie\Valuestore\Valuestore;
and
public function __construct()
{
parent::__construct();
$this->settings = Valuestore::make(storage_path('app/settings.json'));
}
and
public function handle()
{
if($this->settings->get('foo') == 'test') // This works in this custom command!
{
$controller = new MyController;
MyJobName::dispatch($controller);
}
}
So in CustomCommand I use Valuestore in exactly the same way as in MyJobName but in the latter it doesn't work.
As per one of the comments: I do not make $this->settings global as I don't do that in CustomCommand either and it works fine there.
Update 2
If I add protected $settings; above the __construct() function as per the comments it still doesn't work, same error.
Just declare the settings property as public in your Job Class.
public $settings;
public function __construct()
{
$this->settings = Valuestore::make(storage_path('app/settings.json'));
}
I recently had this error. I've tried to make the variables public, delete all the variable inside the Jobs class and even rename and delete the class itself. But it didn't work.
Shortly, I run this artisan command php artisan optimize:clear to clear all the caches, views, routes, etc. And it somehow solve the problem about variable in my problem. For anyone who is still have this OP's problem, give a try to my solution above.
If you use JOB by QUEUE, you need all the requests or SQL queries to do by the method handle
public function handle()
{
$this->settings = Valuestore::make(storage_path('app/settings.json'));
....
}
Because the constructor works when you make the object of class, and this object is serialized and stored in the database and after the unserialization and the handle is triggered.
You may need to restart your queue worker
From Laravel 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.
If you use a daemon php artisan queue:restart
If you use queue:work on your bash hit Ctrl+C then again php artisan queue:work should be enough

Why do my failed jobs disappear after migration to Laravel 5.8?

I have a Laravel project with a queue, running ~1000 jobs a day.
When a job is failing, in 5.7, it was moved to failed_jobs table, and I was able to retry it.
After migration to Laravel 5.8, jobs just disappear.
This code should mark a job as failed and put it in the failed-jobs table.
/**
* Execute the job.
*/
public function handle()
{
throw new \Exception('WRONG JOB');
}
Supervisor log in that case:
[2019-04-10 15:07:57][11932] Processing: App\Jobs\ExecuteAction
It seems that execution stops, and Queue::failing event is not called.
This code works, but doesn't seem right to me.
class ExecuteAction implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Execute the job.
*/
public function handle()
{
try {
throw new \Exception('WROOONG');
} catch (\Exception $e) {
if ($this->attempts() < $this->tries) {
$this->release(10);
} else {
$this->fail($e);
}
}
}
}
Queue::failing event is called.
Supervisor log ;
[2019-04-10 15:06:52][11926] Processing: App\Jobs\ExecuteAction
[2019-04-10 15:06:52][11926] Failed: App\Jobs\ExecuteAction
[2019-04-10 15:06:52][11926] Processed: App\Jobs\ExecuteAction
In supervisor conf
command=php /path/to/laravel-project/artisan queue:listen
What I am doing wrong ?
Thank you for your help.
From: https://laravel.com/docs/5.8/queues
The command: queue:listen was removed from the documentation and now the recommended way to work with queues is to run: queue:work.
See: https://laravel.com/docs/5.8/queues#running-the-queue-worker
Or you may use: Laravel Horizon

Getting Queued Jobs response Laravel 5.2

currently I have the following set up, a route that is calling a function in my controller that is in turn queuing a job.
//My Route
Route::get('/testJob', 'Controller#testJob');
//My Controller
public function testJob()
{
$job = (new testJob())->delay(5);
$this->dispatch($job);
}
//My job
public function handle()
{
require 'testAPICall.php';
// echo $response;
return $response;
}
//testAPICall.php
$response = 'this is the response';
//Queue After
Queue::after(function (JobProcessed $event) {
echo var_dump($event->data);
});
What I would like to be able to do, is access the response returned by the job in Queue::after, or alternatively, pass a callback into the queue to be execute after the job, again with access to the response from the job.
Is this something that is possible with Laravel Queues, and if so how would I go about this?
Cheers, Jack.
Queue::after() is a global callback, that will run after each job. So this might not what you want.
In your case, I would depend on Events/Listeners to be triggered after finishing the job.
public function handle(Mailer $mailer)
{
//Your code
event(new JobDone($data));
}
Please let me know if you need more details for implementation.
I have done something like yours that log a message "queue.txt" in laravel 5 "app" folder
This code I've got from a youtube video and not my code , but I have tested it successfully
First thing you have to code in "Routes.php" as below
Route::get('/',function()
{
//$queue = Queue::push('LogMessage',array('message'=>'Time: '.time()));
$queue = Queue::later(20,'LogMessage',array('message'=>'Time: '.time()));
return $queue;
});
class LogMessage{
public function fire($job,$data){
File::append(app_path().'/queue.txt',$data['message'].PHP_EOL);
$job->delete();
}
}
Then you can run your project folder using "php -S localhost:8888 -t public"
at the same time you must open a another terminal window in windows or linux environment and pointed to same folder and issue the command "php artisan queue:listen"
I think this will be helpful for you!

Resources