First I need to understand how Magento cron works.
I know how cron works on linux, using crontab -e.
I know I need to set up Magento's cron.php to run periodically
But when I define cron within magento config file, how does they match that they should be run?
Because if set my cron.php to run every 15 min (0,15,30,45 * * * *) and I have a Magento cron <schedule><cron_expr>10 * * * *</cron_expr></schedule> for example.
How will it match/work?
By the way, how could I test that my cron works without waiting a long time?
Magento's cron entry point is the cron.php script in your Magento root. You'll need to setup an OS crontab entry hit this script, which you appear to have done already.
How the cron works
Every time the cron.php script is hit three things happen:
Schedule: Magento parses the merged config.xml files for <config><crontab>...</crontab></config> jobs entries, reading their cron_expr elements for detail on how often they should be run. Magento then populates the cron schedule table with jobs that should be executed in the future, along with timestamps for when they should be run. The extent into the future that Magento does this is configurable in the admin.
Execute: Magento reads the cron schedule table for jobs that need to be executed this very second and jobs that should have already been executed, i.e. with timestamps in the past, that haven't expired. The expiry limit is also a parameter configurable in the admin.
Cleanup: Magento goes through the schedule table deleting jobs that have been completed, or missed (due to the deadline).
Duplicate runs?
You raise an interesting point. What happens if you have a 10 minute cron scheduled in Magento, but cron.php is only guaranteed to be hit every 15 minutes. It appears that Magento would run your Magento jobs twice, in some cases:
HH:00 -> HH:50 and HH:00
HH:15 -> HH:10
HH:30 -> HH:20 and HH:30
HH:45 -> HH:40
I've only read the code, and that's what appears to happen. The best way to find out would be, of course, to run it and find out. If you want to avoid the issue, in most cases, then increase the cron.php execution frequency to every 5 minutes, which is what we do.
Testing your code
If you want to test if your cron is actually working without waiting an age, set the cron_expr to execute every minute, or clear out your cron schedule table then hit cron.php twice (once to generate the schedule, and again to run the job).
If you want to just test that your model is working, you could setup a test script (outlined in https://www.nicksays.co.uk/testing-magento-modules), and run that.
Your question prompted a blog post: https://www.nicksays.co.uk/dissecting-the-magento-cron-system :)
On the second half of your question... how to tell if its running.
Here is something that I did. I added the following code at the end of the cron.php file. This updates a simple log called "cronlog.txt" everytime the cron.php file gets called.
So, if I have doubts about cron running I can just glance at that file and see the last date and time the cron.php file ran.
try {
$myFile = "cronlog.txt";
$fh = fopen($myFile, 'w');
$stringData = date('l jS \of F Y h:i:s A');
fwrite($fh, $stringData);
fclose($fh);
} catch (Exception $e) {
Mage::printException($e);
}
adding these to the bottom of cron.php will make it write out cron.php.log on execution with the datetime
try {
Mage::getConfig()->init()->loadEventObservers('crontab');
Mage::app()->addEventArea('crontab');
Mage::dispatchEvent('default');
$log = fopen(__FILE__.'.log', 'a');
fwrite($log, date("Y-m-d H:i:s").PHP_EOL);
fclose($log);
} catch (Exception $e) {
Mage::printException($e);
}
If you're on GNU/Linux you can check /var/log/syslog and filter out the cronjobs by using grep. To see a list of all cronjobs that were run, type in grep 'cron' /var/log/syslog.
To test cron working or not you can simply write Mage::log('cron working', null, 'cron.log'); at the end of the cron.php file.
If the cron is working, it will create a cron.log file in your base dir.
Related
I have created a plugin that adds products programatically to WooCommerce. The plugin is working great, but now I need to make a cron job that runs every 5 minutes to update the inventory.
I have the script all written but I need to include calls to get_option() in this php file to get certain plugin values that the user has entered. However, I can't just include get_option() in this file because it is outside of the Wordpress core. So my thought would be to put in require( 'path/to/wp-load.php' ); which I know you aren't really supposed to do. Anyway it fixes the issue if you hit the page via a web browser request. However the cron job fails the moment that this file is included because somewhere with wp-load.php it is sending HTTP_Header requests.
Any thoughts or solutions? I tried to add define('WP_USE_THEMES', false); right above the requiring of wp-load.php but it is still causing the cron job to fail.
Long winded I know, but how do you include get_option() requests inside of a external PHP script that will be accessed via a PHP cron job.
Thanks much.
The quick and easy way
The problem is probably that you try to include wp-load.php from a wrong path. In a CLI environment, the path would not be the same as when you do an HTTP request to the file. So with this you should fixed your issue:
require(dirname(__FILE__) . '/../../../wp-config.php');
The proper but longer way
Based on cale_b comments and this article he linked, there is a much proper way to go by doing a Wordpress Cron job.
First in your plugin add a function that will contain the code needed to be executed, let's call it my_cron_job(). You can eventually just include the script you already wrote in this function. Then add the following to schedule the execution of this every 5min:
// Define a new interval (5 minutes)
add_filter('cron_schedules', 'fively_interval');
function fively_interval($interval) {
$interval['fively'] = array('interval' => 5*60, 'display' => 'Once 5 minutes');
return $interval;
}
// Register the hook on plugin activation
register_activation_hook(__FILE__, 'my_cron_job_activation');
add_action('my_cron_event', 'my_cron_job');
function my_cron_job_activation() {
wp_schedule_event(time(), 'fively', 'my_cron_event');
}
// Unregister the hook on plugin deactivation
register_deactivation_hook( __FILE__, 'my_cron_job_deactivation' );
function my_cron_job_deactivation(){
wp_clear_scheduled_hook( 'my_cron_event' );
}
Then set up your cron to execute wp-cron.php every 5 minutes:
*/5 * * * * php-cli -f [path to your WP]/wp-cron.php
Update
First when choosing the option of executing wp-cron.php with a server cron you should disable the default WP Cron behaviour (execution of cron through web visits):
define('DISABLE_WP_CRON', true);
Secondly, as for your question about WP Cron reliability I see a potential flaw indeed. I'm not 100% sure of that, but I think it is possible that wp_schedule_event get desynchronized with the server cron, as the job get executed only if the interval is past. As it will be re-scheduled depending of the execution time of the script which is slightly different with the server cron time.
For example:
00:00:00:000 Server cron execute wp-cron.php
00:00:00:100 The job can be executed, so let it run
00:00:00:200 Wordpress finished to execute the job - it schedule the event in 5min
00:05:00:000 Server cron execute wp-cron.php
00:05:00:100 The job is planned for 00:05:00:200, no execution !
00:10:00:000 Server cron execute wp-cron.php
00:10:00:100 The job is executed
That's theory of course, maybe this is not accurate. I suggest doing some test and see how it behave. If it indeed behave like I think it did, I suggest as easy workaround to change the wp_schedule_event to a lower interval - 4min for example.
add_filter('cron_schedules', 'fourly_interval');
function fourly_interval($interval) {
$interval['fourly'] = array('interval' => 4*60, 'display' => 'Once 4 minutes');
return $interval;
}
So we'll have the following:
00:00:00:000 Server cron execute wp-cron.php
00:00:00:100 The job can be executed, so let it run
00:00:00:200 Wordpress finished to execute the job - it schedule the event in 4min
00:05:00:000 Server cron execute wp-cron.php
00:05:00:100 The job is planned for 00:04:00:200, so let it run!
00:10:00:000 Server cron execute wp-cron.php
00:00:00:200 Wordpress finished to execute the job - it schedule the event in 4min
00:10:00:100 The job is executed (planned for 00:09:00:200)
With the default WP Cron behaviour disabled it should work flawlessly.
I want to run my cron script at sheduled intervels created at blocks/plugin_name/cron.php . Please help me how to create and call this cron script to run at regular intervels.
Cron.php is the old way, you should create a function plugin_name_cron() in blocks/plugin_name/lib.php
Then in version.php you have a cron parameter which will tell Moodle to run the plugin every x seconds.
$plugin->cron = 0; // Seconds.
So every 15 minutes is
$plugin->cron = 15*60; // Seconds.
0 seconds means never run the cron.
This does depend on the admin cron being run too. If the admin cron is run ever 60 minutes then your plugin cron will only be run every 60 minutes.
For testing you can run the admin cron manually from http://yoursite.com/admin/cron.php
If you want to schedule the cron to run at a certain time, then you will need to add some code to your cron function to work out the scheduled time.
EDIT:
Actually the cron function is slightly different for blocks. Create a function cron() in your class class block_plugin_name extends block_base..
UPDATE:
From Moodle 2.7+, the above has been replaced with scheduled tasks https://docs.moodle.org/dev/Task_API#Scheduled_task_usage
I wanted to implement two cronjobs with different execution time. One cron job is for sending emails and second cron job for validating my application subscriptions.
I write one crontab file and write to two cronjob as follows:
2 * * * * path to mailCronjob mail.php
20 * * * * path to check my application's subscriptions sub.php
The problem is first cronjob is working fine. Mail will delivers fine, but the second cronjob
is not working. I tried to run second job manually, its also working fine.
I am using command to set cronjob as:
crontab crontab_file
when I give command crontab -l
it also shows both cronjob in command line.
I wanted to ask, am I missing something here, or what should I do to run those cronjobs.
FACT: you can run as many cron jobs from a single crontab file as you wish.
FACT: you can also run different jobs as different users, each with their own crontab file.
SUGGESTION:
1) Just debug what's wrong with your second job.
2) It could be path, it could be permissions; it's more than likely environment (the environment for "cron" can be different from the environment for the same user from a command line).
PS:
Try this, too:
How to simulate the environment cron executes a script with?
Debugging crontab jobs
Check the owning user's email and see if an error report has been sent to it.
If you need to be a certain user and have that user's environment change your call to
su - -c "/path/to/sub.php" SubScriptUser
If your script only works from a certain directory use
cd /path/to/ && ./sub.php
I recall the same problem. For some reason, I had to press enter after my second cron job. Even in the first cron job, if it is the only job, needs a CR/LF (enter). Cursor needs to be on second line (if there is one cron job)... cursor needs to be on the third line, if there is two cron jobs. I guess, needs to read the file completely to interpret the cron job command. I just share this, because if you dont do that, only the first job will be executed, and skips the second one totally unless enter is pressed at the end of the second job. Best regards and cheers... Test that and let us know.
You need to add an empty line to the end of the config file
I never did 2 actuall cronjobs in one cron-tab file, but rather had the one cronjob execute every 15 minutes and query the database or look into a config file what tasks are there to execute, maybe this concept helps you.
I am trying to add a cronjob in the kloxo based user interface,
So i navigate into cron scheduled tasks -> add standard -> Select x minutes, select 'all' in all other fields (hour, day, week, month, ..), then add the path in the last field: /home/piscolab/public_html/keepyourlinks.com/includes/boot.php
But the file is never executed,
what am i missing?
find the problem: check the cron log. if you dont know where your cron log is going to check the syslog. if you cant find anything there kill the cron proccess and start a new one with the -L /yourlogpath/ parameter specified. wait till the job is supposed to run and check the log after.
fix your problem or come back here to get help
as i see you dont specify what program should run the *.php file. change that to whatever program you want to run the php file.
if you use linux and the gui/webgui sux, fall back to shell. cron jobs are defined within /etc/spool/crond/crontab/root (or something similar to that, cant remember the exact path right now, but using TABSTOP will do the job for you). the crontab syntax can be seen here and here. Cron newbies go here as well.
I'm working on a wamp development environment and testing how long it takes to get the site indexed. I'm doing this by running cron manually.
The problem is that if there's 700 jobs in the job_queue, each run of cron does only some of them, so I need to run cron several times. How could I keep calling cron in a loop until there are no more jobs left in the job_queue?
Also, I'm open to any drush alternatives. I'm aware of drush cron, but this also does only some of the jobs each run, so needs to be run again manually.
If you want to run something all at once until it's done, cron is the wrong tool for the job; batch API is the tool for that. Unfortunately the search module is written to update only on cron, but there's not a lot of code in search_cron to copy into a batch function. So I'd suggest going the batch route rather wrapping some sort of pseudo-batch around cron, as your end goal doesn't seem to involve cron at all.
step 1. - Run cron every minute
step 2. - Just check if the script is already running. If so - close the "new script"
$lid='';
if(is_array($argv) && isset($argv[1])) {
$lid=$argv[1];
}
if($lid!=='') {
$xx=array();
exec('ps x | grep "thefile.php '.$lid.'" | grep -v "grep"', &$xx);
if(count($xx)>1) {
die('Script '.$lid.' running already... exiting... '."\n");
}
}
Put it in cron with every minute php thefile.php 1
php thefile.php 2
php thefile.php 3 to run 3 scripts at the same time.
drush search-index will generate the remaining search index for you.