mirror of
https://github.com/Combodo/iTop.git
synced 2026-07-18 12:46:38 +02:00
N°4692 - Enable parallelization of multiple CRON jobs
This commit is contained in:
@@ -3218,4 +3218,24 @@ TXT
|
||||
return (int)$sLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of cron.php processes currently running
|
||||
*
|
||||
* @return int process count
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CountCronRunning(): int
|
||||
{
|
||||
$iMaxCronProcess = max(MetaModel::GetConfig()->Get('cron.max_processes'), 1);
|
||||
$iCount = 0;
|
||||
for ($i = 0; $i < $iMaxCronProcess; $i++) {
|
||||
$oMutex = new iTopMutex("cron#$i");
|
||||
if ($oMutex->IsLocked()) {
|
||||
$iCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return $iCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,8 +578,8 @@ class Config
|
||||
'cron_max_execution_time' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Duration (seconds) of the cron.php script : if exceeded the script will exit even if there are remaining tasks to process. Must be shorter than php max_execution_time setting (note than when using CLI, this is set to 0 by default which means unlimited). If cron.php is ran via web, it must be shorter than the web server response timeout.',
|
||||
'default' => 600,
|
||||
'value' => 600,
|
||||
'default' => 590,
|
||||
'value' => 590,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
|
||||
@@ -32,7 +32,9 @@ class CronLog extends LogAPI
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = []): void
|
||||
{
|
||||
$sMessage = 'cron'.str_pad(static::$iProcessNumber, 3).$sMessage;
|
||||
if (static::$iProcessNumber !== 0) {
|
||||
$sMessage = 'cron'.str_pad(static::$iProcessNumber, 3).$sMessage;
|
||||
}
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,7 @@ if (file_exists(READONLY_MODE_FILE)) {
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
$sConfigFile = APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE;
|
||||
$sConfigFile = utils::GetConfigFilePath();
|
||||
if (!file_exists($sConfigFile)) {
|
||||
echo "iTop is not yet installed. Exiting...\n";
|
||||
exit(EXIT_CODE_ERROR);
|
||||
@@ -53,7 +51,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
$sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
|
||||
if (is_null($sValue)) {
|
||||
$oP->p("ERROR: Missing argument '$sParam'\n");
|
||||
CronLog::Error("ERROR: Missing argument '$sParam'\n");
|
||||
UsageAndExit($oP);
|
||||
}
|
||||
|
||||
@@ -191,7 +189,15 @@ function CronExec($bDebug)
|
||||
CronLog::Trace("Planned duration per task = $iTimeSlot seconds");
|
||||
CronLog::Trace("Loop pause = $iCronSleep seconds");
|
||||
|
||||
ReSyncProcesses($bDebug);
|
||||
$oResyncMutex = new iTopMutex("cron_resync_processes");
|
||||
try {
|
||||
if ($oResyncMutex->TryLock()) {
|
||||
ReSyncProcesses($bDebug);
|
||||
$oResyncMutex->Unlock();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
CronLog::Error('Error: '.$e->GetMessage(), CronLog::CHANNEL_DEFAULT, ['stack' => $e->getTraceAsString()]);
|
||||
}
|
||||
|
||||
while (time() < $iTimeLimit) {
|
||||
CheckMaintenanceMode();
|
||||
@@ -436,6 +442,7 @@ function ReSyncProcesses($bDebug)
|
||||
// Main
|
||||
//
|
||||
|
||||
CronLog::Enable(APPROOT.'/log/itop-cron.log');
|
||||
set_time_limit(0); // Some background actions may really take long to finish (like backup)
|
||||
$bIsModeCLI = utils::IsModeCLI();
|
||||
if ($bIsModeCLI) {
|
||||
@@ -464,7 +471,7 @@ try {
|
||||
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
|
||||
UserRights::Login($sAuthUser); // Login & set the user's language
|
||||
} else {
|
||||
$oP->p("Access wrong credentials ('$sAuthUser')");
|
||||
CronLog::Error("Access wrong credentials ('$sAuthUser')");
|
||||
$oP->output();
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
@@ -474,7 +481,7 @@ try {
|
||||
}
|
||||
|
||||
if (!UserRights::IsAdministrator()) {
|
||||
$oP->p("Access restricted to administrators");
|
||||
CronLog::Error("Access restricted to administrators");
|
||||
$oP->Output();
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
@@ -487,12 +494,11 @@ try {
|
||||
|
||||
require_once(APPROOT.'core/mutex.class.inc.php');
|
||||
} catch (Exception $e) {
|
||||
$oP->p("Error: ".$e->GetMessage());
|
||||
CronLog::Error("Error: ".$e->GetMessage(), CronLog::CHANNEL_DEFAULT, ['stack' => $e->getTraceAsString()]);
|
||||
$oP->output();
|
||||
exit(EXIT_CODE_FATAL);
|
||||
}
|
||||
|
||||
CronLog::Enable(APPROOT.'/log/error.log');
|
||||
try {
|
||||
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE)) {
|
||||
CronLog::Debug("A maintenance is ongoing");
|
||||
@@ -511,11 +517,13 @@ try {
|
||||
}
|
||||
if ($bCanRun) {
|
||||
CronLog::$iProcessNumber = $iProcessNumber;
|
||||
CronLog::Debug('Starting: '.time().' ('.date('Y-m-d H:i:s').')');
|
||||
$iCronRunningCount = utils::CountCronRunning();
|
||||
CronLog::Info("Starting ($iCronRunningCount total cron running)");
|
||||
CronExec($iVerbose > 0);
|
||||
} else {
|
||||
CronLog::$iProcessNumber = $iMaxCronProcess + 1;
|
||||
CronLog::Trace("The limit of $iMaxCronProcess cron process running in parallel is already reached");
|
||||
CronLog::$iProcessNumber = 0;
|
||||
CronLog::Debug("The limit of $iMaxCronProcess cron process running in parallel is already reached");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -524,9 +532,10 @@ try {
|
||||
try {
|
||||
$oMutex->Unlock();
|
||||
} catch (Exception $e) {
|
||||
$oP->p("ERROR: '".$e->getMessage()."'");
|
||||
CronLog::Error("ERROR: '".$e->getMessage()."'");
|
||||
}
|
||||
}
|
||||
|
||||
CronLog::Debug("Exiting: ".time().' ('.date('Y-m-d H:i:s').')');
|
||||
$iCronRunningCount = utils::CountCronRunning();
|
||||
CronLog::Info("Exiting ($iCronRunningCount total cron running)");
|
||||
$oP->Output();
|
||||
|
||||
79
webservices/cron_multi_launcher_linux.php
Normal file
79
webservices/cron_multi_launcher_linux.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\WebPage\CLIPage;
|
||||
use Combodo\iTop\Service\Cron\CronLog;
|
||||
|
||||
require_once(__DIR__.'/../approot.inc.php');
|
||||
|
||||
$sConfigFile = utils::GetConfigFilePath();
|
||||
if (!file_exists($sConfigFile)) {
|
||||
echo "iTop is not yet installed. Exiting...\n";
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
|
||||
function UsageAndExit()
|
||||
{
|
||||
echo "USAGE:\n";
|
||||
echo "php cron_multi_launcher_linux.php --param_file=<file>\n";
|
||||
exit(EXIT_CODE_FATAL);
|
||||
}
|
||||
|
||||
function ReadMandatoryParam($sParam, $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
$sValue = utils::ReadParam($sParam, null, true, $sSanitizationFilter);
|
||||
if (is_null($sValue)) {
|
||||
echo "ERROR: Missing argument '$sParam'\n";
|
||||
UsageAndExit();
|
||||
}
|
||||
|
||||
return trim($sValue);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main
|
||||
//
|
||||
|
||||
try {
|
||||
utils::UseParamFile();
|
||||
$sAuthUser = ReadMandatoryParam('auth_user', 'raw_data');
|
||||
$sAuthPwd = ReadMandatoryParam('auth_pwd', 'raw_data');
|
||||
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
|
||||
UserRights::Login($sAuthUser); // Login & set the user's language
|
||||
} else {
|
||||
echo "Access wrong credentials ('$sAuthUser')\n";
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
if (!UserRights::IsAdministrator()) {
|
||||
echo "Access restricted to administrators\n";
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE) || file_exists(MAINTENANCE_MODE_FILE) || file_exists(READONLY_MODE_FILE)) {
|
||||
echo "A maintenance is ongoing\n";
|
||||
exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
$iMaxCronProcess = max(MetaModel::GetConfig()->Get('cron.max_processes'), 1);
|
||||
$iRespawnTime = 60 / $iMaxCronProcess;
|
||||
for ($i = 0; $i < $iMaxCronProcess; $i++) {
|
||||
$sParamsFile = utils::ReadParam('param_file', '', true, 'raw_data');
|
||||
$sCronCmd = APPROOT."webservices/cron.php --param_file=$sParamsFile";
|
||||
$sOutputFile = APPROOT.'log/itop-cron.log';
|
||||
|
||||
// Execute command, redirect stdout and stderr
|
||||
exec(sprintf('/usr/bin/php %s >> %s 2>&1 &', escapeshellcmd($sCronCmd), escapeshellarg($sOutputFile)));
|
||||
sleep((int)(floor($iRespawnTime)));
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "ERROR: {$e->getMessage()}\n";
|
||||
}
|
||||
Reference in New Issue
Block a user