Laravel Artisan: How does `schedule:run` work? - laravel

I have a dummy Command job set up, whose handle() function is as follows:
public function handle()
{
$this->line('==================');
$this->line('Running my job at ' . Carbon::now());
$this->line('Ending my job at ' . Carbon::now());
}
As you see, it doesn't actually do anything but return a few lines of info to the standard output.
Now, in my App\Console\Kernel class, I have set up the following schedule:
protected function schedule(Schedule $schedule)
{
$schedule
-> command('cbh:dummyCommand')
-> everyMinute()
-> appendOutputTo (storage_path().'/logs/laravel_output.log');
}
Now, from the command-line I run php artisan schedule:run. The output in my laravel_output.log file reads
==================
Running my job at 2018-02-08 11:01:33
Ending my job at 2018-02-08 11:01:33
So far so good. It seems that my schedule is running. However, if I run the command again within the same minute, my logfile now reads:
==================
Running my job at 2018-02-08 11:01:33
Ending my job at 2018-02-08 11:01:33
==================
Running my job at 2018-02-08 11:01:51
Ending my job at 2018-02-08 11:01:51
In other words, the schedule appears to be running more frequently than every minute, which appears to me to break the rules I defined in my schedule.
What's more confusing is that I can change the schedule to run every 5 minutes instead of every minute:
protected function schedule(Schedule $schedule)
{
$schedule
-> command('cbh:dummyCommand')
-> everyFiveMinutes()
-> appendOutputTo (storage_path().'/logs/laravel_output.log');
}
then run php artisan schedule:run, then I get the following output
No scheduled commands are ready to run.
I can wait as long as you like (i.e. more than 5 minutes) and still I get no output to my log file.
I observe exactly the same behaviour when I schedule my command with Windows Task Scheduler (yes, my development environment is a Windows 7 box, and yes, this is the Windows equivalent of a cron-job).
The Question
So what's going on? How does the artisan schedule:run command figure out which commands are "waiting" on the schedule to be executed? I had imagined that there would be some kind of log-file to record the fact that "Command X is on a 1-hour schedule and last ran at 09:00, so don't execute it again before 10:00", but I have been able to find no trace of such a log.
Can someone give me a clue?
Thanks!!

Not cool to answer your own question, I know. Anyhow, let's imagine this is my schedule:
protected function schedule(Schedule $schedule)
{
$schedule
-> command('cbh:dummyCommand')
-> everyFiveMinutes()
-> appendOutputTo ('/my/logs/laravel_output.log');
}
What I've discovered is that this code doesn't set your job to run every 5 minutes. Nor does it prevent the command running again if it was run less than 5-minutes ago.
A better way to think about it is that this code sets the named command "to be runnable every time the minute-figure of the current time is 0 or 5". In other words, if I run the command-line argument: php artisan schedule:run at 11:04, then the response is:
# No scheduled commands are ready to run.
But if I run the same command at 11:00 or 11:05, then we get:
# Running scheduled command: php artisan cbh:dummyCommand >> /my/logs/laravel_output.log 2>&1
And I end up with output in my log-file.
I discovered the above when my everyFiveMinutes() schedule was creating a log in my file every 10 minutes based on the fact that my task-scheduler was running every 2 minutes.

I'm answering this just to let other people know it (since I was having the same confusion).
Laravel scheduler does exactly the same job than Linux cron, by checking if a task cronned time (in minutes) is exactly the same of current time.
When you set in crontab * * * * * ... php artisan schedule:run >> ... you are running schedule:run every minute at 0 secs, like '1:00:00', '1:01:00', '1:02:00', etc.
So, if you set your command to run (let's say) on 'mondays at 1:00' in your Laravel scheduler, and you are on a monday at 1:00, it will be executed, regardless the current seconds. And the last part (seconds) is important to understand how it works.
For example, you are on monday at 1:00:05 (5 seconds after 1:00), so cron already launched schedule:run and your task is being executed. Then you open your terminal, go to your project's root directory and launch, manually, php artisan schedule:run. At that time, it may be 1:00:30 (30 seconds after 1:00). Well, now your task will be executed again because 1:00:30 is still part of 1:00. So you can execute N times schedule:run at 1:00 and it will execute N times your task scheduled to run at 1:00.
And that's the magic of no needing a table or a file to control process launching time. Minutes is the minimum unit in cron, so unless you are doing the things wrong (like duplicating schedule:run line, a hack to run a command more often than a minute, etc.) your Laravel tasks will be executing once at the desired time.
Just a note: Check that your timezone is correct in config/app.php. I got crazy to understand why things like everyMinute(), everyFiveMinutes() were working, and dailyAt('1:10') were not. Of course, with Laravel in UTC and me being in GMT-3 (server clock), I had a great difference in hours.

Related

Hourly cron Job Excuting 60 times

I am trying to send an email hourly using the Task scheduler in (laravel) framwork,
the command I am typing in the kernal.php:
$schedule->command('cron:activeUsers')->hourly()->withoutOverlapping();
than put the following in crontab -e:
cd /testEmail && php artisan schedule:run >> /dev/null 2>&1
but I am getting like 60 or even 100 email instead of one,
I put some logs to see what is happening:
the cron job is excuting and sending email on the first 60 second of the first minute of each hour like the following for example:
12:00:00
12:00:01
12:00:02
12:00:03
12:00:04
12:00:05
.
.
12:00:59
how I can prevent the cron job to be excuted like this ? I want the cron job to be excuted only once each hour.
Am I using the wrong method to send the email?
The schedule:run command is supposed to be called every minute (as fast as CRON allows), like so:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
See https://laravel.com/docs/9.x/scheduling#running-the-scheduler
Then, your schedule() function in app/Console/Kernel.php defines the interval for each of your tasks, so your ->hourly() command should execute at the start of every hour.
If it is executing any faster than that, its likely that you somehow happen to call the scheduler more often than once a minute (multiple CRONs maybe for the same command?). Try a debug statement in the schedule() function in app/Console/Kernel.php to see that it actually executes once a minute.
When you put a debug statement inside your actual command (i.e. 'cron:activeUsers'), then you should see that it runs every hour.
59 * * * *
repeat at [Minutes] [Hour] [Day of Month] [Which Month] [Which Week]
use the above value to do it every 60 minutes, If want to check it, you can also use https://crontab.guru/ to verify what exactly you want.
at 59, it tells to redo it every 59 minutes, plus have mentioned already there about the star astreriks for you to test and check along with the url of resource

How to run queue worker on shared hosting

My Laravel application has a queued event listener and I have also set up the cronjob to run schedule:run every minute.
But I don't know how I can run the php artisan queue:worker command persistently in the background. I found this thread where it was the most voted approach:
$schedule->command('queue:work --daemon')->everyMinute()->withoutOverlapping();
However, on a different thread some people complained that the above-mentioned command creates multiple queue worker.
How can I safely run a queue worker?
Since Laravel 5.7, there's a new queue command to stop working when empty:
php artisan queue:work --stop-when-empty
As this is mostly just for emails or few small jobs, I put it on a cronjob to run every minute. This isn't really a solution for more than 100 jobs per minute I'd say, but works for my emails. This will run about 5 seconds every minute just to send emails, depending on how many emails or how big the job.
Steps
Create new command: php artisan make:command SendContactEmails
In SendContactEmails.php, change: protected $signature = 'emails:work';
In the handle() method, add:
return $this->call('queue:work', [
'--queue' => 'emails', // remove this if queue is default
'--stop-when-empty' => null,
]);
Schedule your command every minute:
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:work')->everyMinute();
// you can add ->withoutOverlapping(); if you think it won't finish in 1 minute
}
Update your cronjobs:
* * * * * /usr/local/bin/php /home/username/project/artisan schedule:run > /dev/null 2>&1
Source
Processing All Queued Jobs & Then Exiting
The --stop-when-empty option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when working Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty:
php artisan queue:work --stop-when-empty
are you using cpanel?
you can set in the Scheduler or Cron Jobs menu.
and set the command in there
You can set a schedule task like this
$schedule->command('queue:work --stop-when-empty')->everyMinute()->withoutOverlapping();

Is Laravel Scheduler stopping execution after repeated errors?

In my Laravel scheduler I have many lines like the following
$schedule->command('commandname01')->everyMinute();
$schedule->command('commandname02')->everyMinute();
$schedule->command('commandname03')->everyMinute();
I have noticed that some command e.g. commandname02 is not running anymore
but commandname01 and commandname03 are running.
How could I restore the full commands execution?
Note: if i log into my container and run
php artisan commandname02
It will work fine.
Suspects:
Is it possible that Laravel scheduler stops executing one of the commands if it goes repeatedly on error?
Is it possible that, if commandname01 takes too long, commandname02 will be skipped at same minute execution?
Any other idea?
"Is it possible that, if commandname01 takes too long, commandname02 will be skipped at same minute execution?"
Yes and the reason is that command are not executed in parallel (multithread is not natively available in PHP) but sequentially within scheduler
So if commandname01 takes more than 60" for exec. commandname02 execution will be skipped in the same minute.
Resource: Laravel : Task Scheduling [ In Parallel ]
Solution I adopted and recommend, as provided in link above, it's to implement parallel task leveraging on crontab.
Launch batches to be run in parallel like:
* * * * * php /var/www/artisan batch01 >> /var/log/laravel-scheduler.log
* * * * * php /var/www/artisan batch02 >> /var/log/laravel-scheduler.log
And in batchNN commands just call your commands that can be run sequentially

Laravel Task Scheduling set to run every minute but it run only once

I am using Laravel Task Scheduling. I defined a custom command and set it to run every minute like:
$schedule->command('dailyk')->everyMinute();
Then I used the following command to run the task:
php /var/www/stockhit/artisan schedule:run 1>> /dev/null 2>&1
I used log to check that my custom command continued to run. However, it is not run every minute. Instead, it just ran once.
How can I make it run every minute, instead of just one time?
See Task Scheduling:
Here is the only Cron entry you need to add to your server:
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
This Cron will call the Laravel command scheduler every minute. Then, Laravel evaluates your scheduled tasks and runs the tasks that are due.
Laravel's task scheduler does not stay in memory, it needs to be run every minute. It will then check which tasks need to be run in that minute and run them.
When you run the task scheduler using PHP it just runs once, it needs cron to run it every minute.
you need to add a cron job. On ubuntu use the command
crontab -e
to open your cron job file, then add
* * * * * php /var/www/stockhit/artisan schedule:run 1>> /dev/null 2>&1

Laravel 5.1 Task Scheduling on Windows

I'm trying to get Laravel 5.1 Task Scheduling working on IIS. When I run a batch file using Windows task manager it will run the task one time only. How can I get ->everyMinute() to work?
Windows batch file:
cd c:\inetpub\myapp
c:\PROGRA~2\PHP\php.exe artisan schedule:run 1>> NUL 2>&1
The kernel:
class Kernel extends ConsoleKernel
{
protected $commands = [
\App\Console\Commands\MyCommand::class,
];
protected function schedule(Schedule $schedule)
{
$schedule->command('test')->everyMinute();
}
}
The command:
public function handle()
{
log::info('test');
}
Take a look at the task scheduler doc.
Starting The Scheduler
Here is the only Cron entry you need to add to your server:
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
This Cron will call the Laravel command scheduler every minute. Then, Laravel evaluates your scheduled tasks and runs the tasks that are due.
In your case, you use the Windows task scheduler instead of Cron but the important thing is to call artisan schedule:run every minute. Each time this command is run, it checks its schedule and runs the due tasks/commands added.
artisan schedule:run does not start a long-running process that stays alive to runs tasks until you kill it. As I said, it needs to be called every minute.
You need to create a scheduled task that will execute that batch file every minute.
To do so :
Press Win + R and run taskschd.msc
In the right panel click Create Basic Task and give it a Name + Description.
Click Next and select Start a Program option, then navigate to the batch file and select it. No need to fill the other fields.
Select "Open the properties of this task..." and then Finish.
On the Trigger tab, you can change between Daily or At Logon (as I do).
Here is the part that's not documented, open the dropbox and insert 1 using the keyboard, this is the only way to set the repeat time at 1 minute (even if the dropdown doesn't list it).
Larevel needs the cronjob to run every minute, otherwise it won't work as expected.
Also check "Indefinitely" to run it forlifetime.
Hope it helps.
The Windows Task Scheduler Help is here, if you run into trouble.
I have a single solution
Create to file Executable xxx.cmd, Open the file and write the next text.
#echo off
echo - = = = Schedule Run Jobs == = = = -
CD d: && CD \xampp\htdocs\folderlaravel && php artisan schedule:run
timeout 86400
CD d: && CD \xampp\htdocs\folderlaravel && "Schedule.cmd"
pause
#cls
What you do is run and run itself in an infinite loop depending on what timeout you are given. In this case 86400 => 1 day.
It is somewhat ambiguous but it works :)
I hope it works for you.
Windows does support Laravel Scheduler but, you've to run the command on your own for multiple times. Since we can't use Windows Task Scheduler to run for every 1 min as we can do with linux crontab. If you're using windows for development environment and want to test if command is working on not you can try this
If you run the
php artisan schedule:run
command for multiple times by giving a min gap for each trial it'll work.
If you want to run directly the command you can follow this.
"path\to\php.exe" "artisan" YourCommand > "NUL" 2>&1 &
You can find path of your php.exe using below step.
Run "where php.exe" in command prompt

Resources