Skip to content

Commit

Permalink
MDL-77186 core: Add a new keepalive setting to cron
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Mar 13, 2023
1 parent 097d2bd commit 1b59967
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 16 deletions.
9 changes: 8 additions & 1 deletion admin/cli/cron.php
Expand Up @@ -43,6 +43,7 @@
'enable' => false,
'disable' => false,
'disable-wait' => false,
'keep-alive' => null,
],
[
'h' => 'help',
Expand All @@ -52,6 +53,7 @@
'e' => 'enable',
'd' => 'disable',
'w' => 'disable-wait',
'k' => 'keep-alive',
]
);

Expand All @@ -72,6 +74,9 @@
-e, --enable Enable cron
-d, --disable Disable cron
-w, --disable-wait=600 Disable cron and wait until all tasks finished or fail after N seconds (optional param)
-k, --keep-alive=N Keep this script alive for N seconds and poll for new tasks
The default value can be set by administrators in:
Site administration > Server > Tasks > Task processing > Keep alive time
Example:
\$sudo -u www-data /usr/bin/php admin/cli/cron.php
Expand Down Expand Up @@ -177,4 +182,6 @@

\core\local\cli\shutdown::script_supports_graceful_exit();

cron_run();

$keepalive = $options['keep-alive'];
cron_run($keepalive);
4 changes: 2 additions & 2 deletions admin/cron.php
Expand Up @@ -77,5 +77,5 @@
// We do not want html markup in emulated CLI.
@ini_set('html_errors', 'off');

// Execute the cron.
cron_run();
// Execute the cron, disabling keepalive.
cron_run(0);
21 changes: 21 additions & 0 deletions admin/settings/server.php
Expand Up @@ -331,6 +331,27 @@
$setting->set_updatedcallback('theme_reset_static_caches');
$temp->add($setting);

// Note: At the moment cron functions live in lib/cronlib.php which is not guaranteed to be loaded everywhere.
// We have no sensible place to put constants.
$setting = new admin_setting_configduration(
'cron_keepalive',
new lang_string('cron_keepalive', 'admin'),
new lang_string('cron_keepalive_desc', 'admin'),

// Use a default value of 3 minutes.
// The recommended cron frequency is every minute, and the default adhoc concurrency is 3.
// A default value of 3 minutes allows all adhoc tasks to be run concurrently at their default value.
3 * MINSECS,

// The default unit is minutes.
MINSECS,
);

// Set an upper limit of 15 minutes.
$setting->set_max_duration(15 * MINSEC);

$temp->add($setting);

$temp->add(
new admin_setting_configtext(
'task_scheduled_concurrency_limit',
Expand Down
2 changes: 2 additions & 0 deletions lang/en/admin.php
Expand Up @@ -441,6 +441,8 @@
$string['cron_enabled'] = 'Enable cron';
$string['cron_enabled_desc'] = 'Cron should normally be enabled, however this setting allows it to be disabled temporarily, for example before a server restart. If disabled, the system is prevented from starting new background tasks. Note that the cron should not be disabled for a long time, as this will prevent important functionality from working.';
$string['cron_help'] = 'The cron.php script runs a number of tasks at different scheduled intervals, such as sending forum post notification emails. The script should be run regularly - ideally every minute.';
$string['cron_keepalive'] = 'Keep alive';
$string['cron_keepalive_desc'] = 'The amount of time to keep polling for additional tasks. This setting is useful for ensuring that cron is always running.<br><br>If you use dedicated task runners then you should set this value to an 0, otherwise we recommend setting this to around a value similar to your adhoc task concurrency limit. Longer values should be avoided and the maximum value is 15 minutes.';
$string['cron_link'] = 'admin/cron';
$string['cronclionly'] = 'Cron execution via command line only';
$string['cronerrorclionly'] = 'Sorry, internet access to this page has been disabled by the administrator.';
Expand Down
71 changes: 59 additions & 12 deletions lib/cronlib.php
Expand Up @@ -25,8 +25,10 @@

/**
* Execute cron tasks
*
* @param int|null $keepalive The keepalive time for this cron run.
*/
function cron_run(): void {
function cron_run(?int $keepalive = null): void {
global $DB, $CFG;

if (CLI_MAINTENANCE) {
Expand All @@ -49,7 +51,6 @@ function cron_run(): void {
}

core_php_time_limit::raise();
$starttime = microtime();

// Increase memory limit.
raise_memory_limit(MEMORY_EXTRA);
Expand All @@ -69,20 +70,66 @@ function cron_run(): void {
set_config('lastcroninterval', max(1, $timenow - $laststart), 'tool_task');
}

// Run all scheduled tasks.
cron_run_scheduled_tasks($timenow);
// Determine the time when the cron should finish.
if ($keepalive === null) {
$keepalive = get_config('core', 'cron_keepalive');
if ($keepalive === false) {
// Use a default value of 3 minutes.
// The recommended cron frequency is every minute, and the default adhoc concurrency is 3.
// A default value of 3 minutes allows all adhoc tasks to be run concurrently at their default value.
$keepalive = 3 * MINSECS;
}
}

// Run adhoc tasks.
cron_run_adhoc_tasks($timenow);
if ($keepalive > 15 * MINSECS) {
// Attempt to prevent abnormally long keepalives.
mtrace("Cron keepalive time is too long, reducing to 15 minutes.");
$keepalive = 15 * MINSECS;
}

mtrace("Cron run completed correctly");
// Calculate the finish time based on the start time and keepalive.
$finishtime = $timenow + $keepalive;

gc_collect_cycles();
do {
$startruntime = microtime();
// Run all scheduled tasks.
cron_run_scheduled_tasks($timenow);

// Run adhoc tasks.
cron_run_adhoc_tasks($timenow);

mtrace("Cron run completed correctly");

$completiontime = date('H:i:s');
$difftime = microtime_diff($starttime, microtime());
$memoryused = display_size(memory_get_usage());
mtrace("Cron completed at {$completiontime} in {$difftime} seconds. Memory used: {$memoryused}.");
gc_collect_cycles();

$completiontime = date('H:i:s');
$difftime = microtime_diff($startruntime, microtime());
$memoryused = display_size(memory_get_usage());

$message = "Cron completed at {$completiontime} in {$difftime} seconds. Memory used: {$memoryused}.";

// Check if we should continue to run.
// Only continue to run if:
// - The finish time has not been reached; and
// - The graceful exit flag has not been set; and
// - The static caches have not been cleared since the start of the cron run.
$remaining = $finishtime - time();
$runagain = $remaining > 0;
$runagain = $runagain && !\core\local\cli\shutdown::should_gracefully_exit();
$runagain = $runagain && !\core\task\manager::static_caches_cleared_since($timenow);

if ($runagain) {
$message .= " Continuing to check for tasks for {$remaining} more seconds.";
mtrace($message);
sleep(1);

// Re-check the graceful exit and cache clear flags after sleeping as these may have changed.
$runagain = $runagain && !\core\local\cli\shutdown::should_gracefully_exit();
$runagain = $runagain && !\core\task\manager::static_caches_cleared_since($timenow);
} else {
mtrace($message);
}
} while ($runagain);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion version.php
Expand Up @@ -29,7 +29,7 @@

defined('MOODLE_INTERNAL') || die();

$version = 2023031000.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2023031000.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.2dev (Build: 20230310)'; // Human-friendly version name
Expand Down

0 comments on commit 1b59967

Please sign in to comment.