Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

N°4692 - Enable parallelization of multiple CRON jobs #304

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
16 changes: 8 additions & 8 deletions core/config.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -512,14 +512,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'cron_task_max_execution_time' => [
'type' => 'integer',
'description' => 'Background tasks will use this value (integer) multiplicated by its periodicity (in seconds) as max duration per cron execution. 0 is unlimited time',
'default' => 0,
'value' => 0,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
Comment on lines -515 to -522
Copy link
Contributor

@Hipska Hipska Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cron_task_max_execution_time is a setting that should stay I assume?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the parameter will stay, but the behaviour will change (the « multiplicated by its periodicity (in seconds) » part is not pertinent anymore)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but the current state of this PR just removes this setting parameter. Or did I miss something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact 'cron_max_execution_time' is yet used, but 'cron_task_max_execution_time' is not used anymore... sorry for the confusion. The limit of execution of one task is the remaining cron execution time as it won't block other tasks anymore.
I'm sorry this project is pretty old now, and I'm not sure it will be integrated soon :(

'cron_sleep' => [
'type' => 'integer',
'description' => 'Duration (seconds) before cron.php checks again if something must be done',
Expand All @@ -528,6 +520,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'cron.max_processes' => [
'type' => 'integer',
'description' => 'Maximum number of cron processes to run',
'default' => 10,
'value' => 10,
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'async_task_retries' => [
'type' => 'array',
'description' => 'Automatic retries of asynchronous tasks in case of failure (per class)',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@
'Combodo\\iTop\\Renderer\\FormRenderer' => $baseDir . '/sources/Renderer/FormRenderer.php',
'Combodo\\iTop\\Renderer\\RenderingOutput' => $baseDir . '/sources/Renderer/RenderingOutput.php',
'Combodo\\iTop\\Router\\Router' => $baseDir . '/sources/Router/Router.php',
'Combodo\\iTop\\Service\\Cron\\CronLog' => $baseDir . '/sources/Service/Cron/CronLog.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => $baseDir . '/sources/Application/Service/Events/Description/EventDataDescription.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDescription' => $baseDir . '/sources/Application/Service/Events/Description/EventDescription.php',
'Combodo\\iTop\\Service\\Events\\EventData' => $baseDir . '/sources/Application/Service/Events/EventData.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Renderer\\FormRenderer' => __DIR__ . '/../..' . '/sources/Renderer/FormRenderer.php',
'Combodo\\iTop\\Renderer\\RenderingOutput' => __DIR__ . '/../..' . '/sources/Renderer/RenderingOutput.php',
'Combodo\\iTop\\Router\\Router' => __DIR__ . '/../..' . '/sources/Router/Router.php',
'Combodo\\iTop\\Service\\Cron\\CronLog' => __DIR__ . '/../..' . '/sources/Service/Cron/CronLog.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDataDescription' => __DIR__ . '/../..' . '/sources/Application/Service/Events/Description/EventDataDescription.php',
'Combodo\\iTop\\Service\\Events\\Description\\EventDescription' => __DIR__ . '/../..' . '/sources/Application/Service/Events/Description/EventDescription.php',
'Combodo\\iTop\\Service\\Events\\EventData' => __DIR__ . '/../..' . '/sources/Application/Service/Events/EventData.php',
Expand Down
2 changes: 2 additions & 0 deletions setup/compiler.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,12 @@ public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = null, $bSk

try
{
SetupLog::Info("Compiling $sTempTargetDir...");
$this->DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks);
}
catch (Exception $e)
{
SetupLog::Info("Compiling error: ".$e->getMessage());
if ($sTempTargetDir != $sFinalTargetDir)
{
// Cleanup the temporary directory
Expand Down
59 changes: 35 additions & 24 deletions setup/setuputils.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -2045,36 +2045,47 @@ public static function IsInReadOnlyMode()
*/
private static function WaitCronTermination($oConfig, $sMode)
{
try
{
$iMaxDuration = $oConfig->Get('cron_max_execution_time');
// Avoid PHP stopping while waiting the cron
set_time_limit($iMaxDuration);
try {
// Wait for cron to stop
if (is_null($oConfig) || ContextTag::Check(ContextTag::TAG_CRON)) {
return;
}
// Use mutex to check if cron is running
$oMutex = new iTopMutex(
'cron'.$oConfig->Get('db_name').$oConfig->Get('db_subname'),
$oConfig->Get('db_host'),
$oConfig->Get('db_user'),
$oConfig->Get('db_pwd'),
$oConfig->Get('db_tls.enabled'),
$oConfig->Get('db_tls.ca')
);
// Limit the number of cron process to run in parallel
$iMaxCronProcess = $oConfig->Get('cron.max_processes');
$iCount = 1;
$iStarted = time();
$iMaxDuration = $oConfig->Get('cron_max_execution_time');
$iTimeLimit = $iStarted + $iMaxDuration;
while ($oMutex->IsLocked())
{
SetupLog::Info("Waiting for cron to stop ($iCount)");
$iCount++;
sleep(1);
if (time() > $iTimeLimit)
{
throw new Exception("Cannot enter $sMode mode, consider stopping the cron temporarily");
$iTimeLimit = time() + $iMaxDuration;
do {
$bIsRunning = false;
// Use all mutexes to check if cron is running
for ($i = 0; $i < $iMaxCronProcess; $i++) {
$sName = "cron#$i";

$oMutex = new iTopMutex(
$sName.$oConfig->Get('db_name').$oConfig->Get('db_subname'),
$oConfig->Get('db_host'),
$oConfig->Get('db_user'),
$oConfig->Get('db_pwd'),
$oConfig->Get('db_tls.enabled'),
$oConfig->Get('db_tls.ca')
);
if ($oMutex->IsLocked()) {
$bIsRunning = true;
SetupLog::Info("Waiting for cron to stop ($iCount)");
$iCount++;
sleep(1);
if (time() > $iTimeLimit) {
SetupLog::Error("Cannot enter $sMode mode, consider stopping the cron temporarily");
throw new Exception("Cannot enter $sMode mode, consider stopping the cron temporarily");
}
break;
}
}
}
} catch (Exception $e) {
} while ($bIsRunning);
}
catch (Exception $e) {
// Ignore errors
}
}
Expand Down
50 changes: 50 additions & 0 deletions sources/Service/Cron/CronLog.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/

namespace Combodo\iTop\Service\Cron;

use LogAPI;
use Page;

/**
* @since 3.1.0
*/
class CronLog extends LogAPI
eespie marked this conversation as resolved.
Show resolved Hide resolved
{
public static $iProcessNumber = 0;
private static $bDebug = false;
private static $oP = null;

const CHANNEL_DEFAULT = 'Cron';
/**
* @inheritDoc
*
* As this object is used during setup, without any conf file available, customizing the level can be done by changing this constant !
*/
const LEVEL_DEFAULT = self::LEVEL_INFO;

protected static $m_oFileLog = null;

public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
{
$sMessage = 'cron'.str_pad(static::$iProcessNumber, 3).$sMessage;
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
}

public static function Debug($sMessage, $sChannel = null, $aContext = array())
{
if (self::$bDebug && self::$oP) {
self::$oP->p('cron'.str_pad(static::$iProcessNumber, 3).$sMessage);
}
parent::Debug($sMessage, $sChannel, $aContext);
}

public static function SetDebug(Page $oP, $bDebug)
{
self::$oP = $oP;
self::$bDebug = $bDebug;
}
}
Loading