diff --git a/application/Espo/Core/CronManager.php b/application/Espo/Core/CronManager.php index d941a218cb..50ad1e556e 100644 --- a/application/Espo/Core/CronManager.php +++ b/application/Espo/Core/CronManager.php @@ -186,6 +186,7 @@ class CronManager } $job->set('status', self::RUNNING); + $job->set('pid', $this->getCronJobUtil()->getPid()); $this->getEntityManager()->saveEntity($job); $this->getEntityManager()->getPdo()->query('UNLOCK TABLES'); diff --git a/application/Espo/Core/Utils/Cron/Job.php b/application/Espo/Core/Utils/Cron/Job.php index a3cef333fe..70320c0917 100644 --- a/application/Espo/Core/Utils/Cron/Job.php +++ b/application/Espo/Core/Utils/Cron/Job.php @@ -28,10 +28,11 @@ ************************************************************************/ namespace Espo\Core\Utils\Cron; -use \PDO; -use \Espo\Core\CronManager; -use \Espo\Core\Utils\Config; -use \Espo\Core\ORM\EntityManager; + +use PDO; +use Espo\Core\CronManager; +use Espo\Core\Utils\Config; +use Espo\Core\ORM\EntityManager; class Job { @@ -41,12 +42,15 @@ class Job private $cronScheduledJob; + private $systemUtils; + public function __construct(Config $config, EntityManager $entityManager) { $this->config = $config; $this->entityManager = $entityManager; $this->cronScheduledJob = new ScheduledJob($this->config, $this->entityManager); + $this->systemUtils = new \Espo\Core\Utils\System(); } protected function getConfig() @@ -64,6 +68,11 @@ class Job return $this->cronScheduledJob; } + protected function getSystemUtils() + { + return $this->systemUtils; + } + public function isJobPending($id) { return !!$this->getEntityManager()->getRepository('Job')->select(['id'])->where([ @@ -181,37 +190,58 @@ class Job */ public function markFailedJobs() { - $currentTime = time(); - $periodTime = $currentTime - intval($this->getConfig()->get('jobPeriod', 0)); + $this->markFailedJobsByPeriod('jobPeriodForActiveProcess'); + $this->markFailedJobsByPeriod('jobPeriod'); + } + + protected function markFailedJobsByPeriod($period) + { + $time = time() - $this->getConfig()->get($period); $pdo = $this->getEntityManager()->getPDO(); $select = " - SELECT id, scheduled_job_id, execute_time, target_id, target_type FROM `job` + SELECT id, scheduled_job_id, execute_time, target_id, target_type, pid FROM `job` WHERE - `status` = '" . CronManager::RUNNING ."' AND execute_time < '".date('Y-m-d H:i:s', $periodTime)."' + `status` = '" . CronManager::RUNNING ."' AND execute_time < '".date('Y-m-d H:i:s', $time)."' "; $sth = $pdo->prepare($select); $sth->execute(); $jobData = array(); - while ($row = $sth->fetch(PDO::FETCH_ASSOC)){ - $jobData[$row['id']] = $row; + + switch ($period) { + case 'jobPeriod': + while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { + if (!$this->getSystemUtils()->isProcessActive($row['pid'])) { + $jobData[$row['id']] = $row; + } + } + break; + + case 'jobPeriodForActiveProcess': + while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { + $jobData[$row['id']] = $row; + } + $additionalSetQuery = ", attempts = 0"; + break; } - $update = " - UPDATE job - SET `status` = '". CronManager::FAILED ."' - WHERE id IN ('".implode("', '", array_keys($jobData))."') - "; - $sth = $pdo->prepare($update); - $sth->execute(); + if (!empty($jobData)) { + $update = " + UPDATE job + SET `status` = '" . CronManager::FAILED . "'" . (isset($additionalSetQuery) ? $additionalSetQuery : "") . " + WHERE id IN ('".implode("', '", array_keys($jobData))."') + "; + $sth = $pdo->prepare($update); + $sth->execute(); - //add status 'Failed' to SchediledJobLog - $cronScheduledJob = $this->getCronScheduledJob(); - foreach ($jobData as $jobId => $job) { - if (!empty($job['scheduled_job_id'])) { - $cronScheduledJob->addLogRecord($job['scheduled_job_id'], CronManager::FAILED, $job['execute_time'], $job['target_id'], $job['target_type']); + //add status 'Failed' to SchediledJobLog + $cronScheduledJob = $this->getCronScheduledJob(); + foreach ($jobData as $jobId => $job) { + if (!empty($job['scheduled_job_id'])) { + $cronScheduledJob->addLogRecord($job['scheduled_job_id'], CronManager::FAILED, $job['execute_time'], $job['target_id'], $job['target_type']); + } } } } @@ -313,4 +343,9 @@ class Job } } } + + public function getPid() + { + return $this->getSystemUtils()->getPid(); + } } \ No newline at end of file diff --git a/application/Espo/Core/Utils/System.php b/application/Espo/Core/Utils/System.php index 975b1c0110..10e1eccc57 100644 --- a/application/Espo/Core/Utils/System.php +++ b/application/Espo/Core/Utils/System.php @@ -134,4 +134,38 @@ class System return $version; } + + /** + * Pet process ID + * + * @return integer + */ + public function getPid() + { + return getmypid(); + } + + /** + * Check if process is active + * + * @param integer $pid + * + * @return boolean + */ + public function isProcessActive($pid) + { + if (empty($pid)) { + return false; + } + + if (!function_exists('posix_getsid')) { + return false; + } + + if (posix_getsid($pid) === false) { + return false; + } + + return true; + } } \ No newline at end of file diff --git a/application/Espo/Core/defaults/systemConfig.php b/application/Espo/Core/defaults/systemConfig.php index 1a02981aea..514f1c4501 100644 --- a/application/Espo/Core/defaults/systemConfig.php +++ b/application/Espo/Core/defaults/systemConfig.php @@ -58,6 +58,7 @@ return array ( 'defaultPermissions' => ), 'jobMaxPortion' => 15, /** Max number of jobs per one execution. */ 'jobPeriod' => 7800, /** Max execution time (in seconds) allocated for a sinle job. If exceeded then set to Failed.*/ + 'jobPeriodForActiveProcess' => 36000, /** Max execution time (in seconds) allocated for a sinle job with active process. If exceeded then set to Failed.*/ 'jobRerunAttemptNumber' => 1, /** Number of attempts to re-run failed jobs. */ 'cronMinInterval' => 4, /** Min interval (in seconds) between two cron runs. */ 'crud' => array( diff --git a/application/Espo/Resources/metadata/entityDefs/Job.json b/application/Espo/Resources/metadata/entityDefs/Job.json index f0da2187b4..55ee9144d9 100644 --- a/application/Espo/Resources/metadata/entityDefs/Job.json +++ b/application/Espo/Resources/metadata/entityDefs/Job.json @@ -39,6 +39,9 @@ "link": "scheduledJob", "field": "job" }, + "pid": { + "type": "int" + }, "attempts": { "type": "int" }, @@ -79,6 +82,9 @@ }, "status": { "columns": ["status", "deleted"] - } + }, + "pid": { + "columns": ["status", "pid"] + } } }