Laravel 5.7 - Kill artisan after certain amount of time - php

I am developing a laravel 5.7 application.
I have created a command that should setup my database:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class TestSetupCommand extends Command
{
protected $signature = 'test:data';
protected $description = 'Basic Setup for Test Data';
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
Artisan::call('migrate:refresh', ['--seed' => '']);
Artisan::call('basis:cc');
Artisan::call('tick:exchange');
$this->info("DB freshly setup: DONE");
$this->info("Coin: DONE");
$this->info("Exchange Scrapped: DONE");
}
}
My problem is that each command takes several minutes to run through. In total it costs me 25 minutes to fill the whole database with data.
I would like to run the commands only for 1 minutes each and kill them afterwards.
Any suggestions how to accomplish this within my laravel command?

I think the best way to do this is to extract these commands into background job. This artisan command then becomes code to queue up that new job (or jobs).
Why? It's very easy to configure jobs to timeout after x amount of time, by overriding a value like so:
<?php
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* The number of seconds the job can run before timing out.
*
* #var int
*/
public $timeout = 120;
}
Also, why are you refreshing the database? That seems.... like a crazy idea unless this is purely an analytics platform (no user data at all). It's probably a bad thing if that refresh command times out - you may look into job chaining so that the refresh command is guaranteed to succeed, then the other commands (new jobs now) have set timeouts.

Related

Laravel Job Overlapping

I am using the laravel middleware withoutOverlapping in laravel but it does not seem to work when jobs are dispatched at the same time (and take < 1 second)
Example
class TestWaitJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public Charge $charge;
public function __construct(Charge $charge)
{
$this->charge = $charge;
}
/**
* #return array
*/
public function middleware(): array
{
return [
(new WithoutOverlapping($this->charge->id))
->releaseAfter(30)
];
}
/**
* #return void
*/
public function handle()
{
$charge = $this->charge->current_state_key_name;
return;
}
}
This is an absolutely simple job. It does nothing and it is what I have used to test.
If I go to my application and dispatch two copies of the job at the same time (using tinker)
TestWaitJob::dispatch(Charge::Find(1)); TestWaitJob::dispatch(Charge::find(1));1;
Both jobs are processed by horizon at the same time.
If I add a super simple sleep(1) line to the handle method of the job I get the expected behaviour which is
Job begins processing and acquires lock
The next job cannot acquire lock so is released back to the queue.
So with this sleep line, I have a job processed immediately (in 1 second) and the next job completes 31 seconds later which matches up exactly with the releaseAfter(30 seconds)
I have been looking at this for hours and everytime I introduce a delay the jobs process as expected but with no delay they process at the same time. The application is financial in nature so I cannot afford to potentially process jobs at the same time
Any help/advice would be greatly appreciated. (ps I am using Redis as cache)

Laravel 5.6 How to schedule email queue

I'm trying to schedule an email to remind users who have to-do tasks due tomorrow. I made a custom command email:reminder. Here is my code in custom command:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Todo;
use Illuminate\Support\Facades\Mail;
class SendReminderEmail extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'email:reminder';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Remind users of items due to complete next day';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
//
/*
* Send mail dynamically
*/
/*
* hardcoded email
*/
Mail::queue('emails.reminder', [], function ($mail) {
$mail->to('example#email.com')
->from('todoreminder#gmail.com', 'To-do Reminder')
->subject('Due tomorrow on your To-do list!');
}
);
$this->info('Reminder email sent successfully!');
}
}
I hardcoded the email for now to test it, but when I ran php artisan email:reminder, I got an exception of
[InvalidArgumentException]
Only mailables may be queued.
I then checked Laravel's documentation, but Task Scheduling and Email Queue are 2 separate topic.
How can I achieve sending email queue with task scheduling in Laravel
5.6 please?
Also how can I pass data, i.e. to-do items in database into my email
view please?
Any help is greatly appreciated!
Using the console kernel to schedule queued jobs is easy to do. Laravel offers several wrapper methods that make the cron integration trivial. Here's a basic example:
$schedule->job(new SendTodoReminders())->dailyAt('9:00');
You should create a command which does exactly as you described, but without the scheduling. You can use the crontab for scheduling or some other task scheduler.
Have you followed Laravel's documentation about mailing? https://laravel.com/docs/5.6/mail
Once you get to the Sending Mail section, you should not create a controller but a command instead.
When that command works, add it to the task scheduler (eg. crontab) to run on a daily basis.
Mail::queue('emails.reminder', [], function ($mail) {
$mail->to('example#email.com')
->from('todoreminder#gmail.com', 'To-do Reminder')
->subject('Due tomorrow on your To-do list!');
}
);
is deprecated since Laravel 5.3. Only the Mailables can Queued and it should implement the ShouldQueue interface.
For running jobs you have to configure the queue driver and run php artisan queue:work

Laravel - Scheduling a Large Task

Trying to run a function in Laravel that's quite large and fetches a lot of data from Google Places API and stores parts in my database as new entries in a table. The problem is it auto-discovers new entries for me near my current entries, and that creates more jobs.
When I just access the command via GET it times out eventually. I've tried running it as a scheduled command with Redis but to be frank I can't seem to figure out how it works. I've created a job, I tried to queue it with dispatch, but then it tries to run it immediately right now and it times out eventually again.
How do I run this large task without it pausing my entire server?
Thanks
Zach
I is really simple and you do not need to stop server. It is just CRON job. Use Laravel Schedule.
When using the scheduler, you only need to add the following Cron entry to your server.
* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
Class example:
namespace App\Console;
use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}
More: https://laravel.com/docs/5.6/scheduling
Nice video about laravel scheduling: https://www.youtube.com/watch?v=mp-XZm7INl8

Laravel simultaneous launch functions

I have such function in my trait:
public function cupPlayMatch(Season $season, $round_id)
{
foreach($season->cups as $cup)
{
$this->cupPlay($cup, $round_id);
}
}
And second cup isstarting playing when first cup finished. How I can start playing all my cups simultaneous?
For the most part, PHP is "synchronous", that means that you theoretically can't make "simultaneous calls" to any function.
However, some workarounds exist to make this work.
PHP is a script language. So when you launch this in your console:
php -r "echo 'Hello World';"
A PHP process is launched, and anything that happen in this process is executed synchronously.
So the solution here, is to launch various PHP processes in order to be able to run functions simultaneously.
Imagine an SQL table where you put all the functions that you want to execute simultaneously. You could then run 10 php processes that would actually work "at the same time".
Laravel provides a solution to this problem out of the box. And as #Anton Gildebrand mentioned it in the comments, it's called "Queues".
You can find the documentation here: https://laravel.com/docs/5.5/queues
The laravel way of doing it, is to create "jobs". Each Job represents a function that you want to execute. Here, your Job would be cupPlay.
Here is the basic example of a job copy pasted from the documentation:
<?php
namespace App\Jobs;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
/**
* Create a new job instance.
*
* #param Podcast $podcast
* #return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* #param AudioProcessor $processor
* #return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}
When you'll have configured your worker driver to run your queues, you'll just need to launch:
php artisan queue:work --queue=high,default
from the command line and it will execute your tasks.
And you can execute as many workers as you want, depending on your needs...
I hope this helps!

Scheduling tasks after polling the database php

I am building web app in PHP which is used to schedule tasks in the future. In the background I am supposed to Poll the database every 2 min to see if there is any upcoming task. How do I implement polling. Javascript solution is not helpful as the user can be on any of the pages on the webapp, but the system should keep pooling the DB.
Cron is one way but still I have to poll the database to create a cron job. What is the best way to implement it?
Thanks
Create a cron job that is executed once every X minutes and make that job check the DB. If there is a new upcoming task, just make the job launch it.
Create an 'execute:tasks' artisan command, so you can poll your database whenever you need to execute your tasks. You should be able to run it this way:
php artisan execute:tasks
Your command will call some controller action (or a class method) to poll the database and find if there are tasks available to be executed.
Then you just need to create a cron job that will execute that artisan command every 2 minutes:
*/2 * * * * php artisan execute:tasks
This is an example of Artisan command:
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ExecuteTasksCommand extends Command {
/**
* The console command name.
*
* #var string
*/
protected $name = 'execute:tasks';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Find and execute available tasks.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
(new ExecuteTasksClass)->findAndExecute();
}
}
You just have to name this file something like app/commands/ExecuteTasksCommand.php
And add this to app\start\artisan.php:
Artisan::add(new ExecuteTasksCommand);

Categories