mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-30 01:04:12 +02:00
Fix job behavior during summer and winter time change (#2954)
ref/IP/52977 `ts_last_attempt`, which is used in the callback function of `React\EventLoop\LoopInterface::addPeriodicTimer`, had data type timestamp and was saved as UTC time but retrieved as system time, and during DST where the clock is moved forward or set back during DST, you could expect time jumps in the retrieved `ts_last_attempt` value. And the `React\EventLoop\LoopInterface::addPeriodicTimer` expects the time source to be monotonous without any such time jumps. This causes the jobs to be erratically scheduled after DST. And this happens mostly when the `ts_last_attempt` saved time time falls in the skipped or time range that was reset. To avoid such time jumps the `ts_last_attempt` datatype is changed to `BIGINT` and the values are saved as Unix time stamp.
This commit is contained in:
commit
cadac72f4c
@ -3,6 +3,7 @@
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Director\Daemon\DaemonUtil;
|
||||
use Icinga\Module\Director\Daemon\Logger;
|
||||
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
|
||||
use Icinga\Module\Director\Db;
|
||||
@ -84,7 +85,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
|
||||
public function run()
|
||||
{
|
||||
$job = $this->getInstance();
|
||||
$this->set('ts_last_attempt', date('Y-m-d H:i:s'));
|
||||
$currentTimestamp = DaemonUtil::timestampWithMilliseconds();
|
||||
$this->set('ts_last_attempt', $currentTimestamp);
|
||||
|
||||
try {
|
||||
$job->run();
|
||||
@ -92,7 +94,7 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
|
||||
$success = true;
|
||||
} catch (Exception $e) {
|
||||
Logger::error($e->getMessage());
|
||||
$this->set('ts_last_error', date('Y-m-d H:i:s'));
|
||||
$this->set('ts_last_error', $currentTimestamp);
|
||||
$this->set('last_error_message', $e->getMessage());
|
||||
$this->set('last_attempt_succeeded', 'n');
|
||||
$success = false;
|
||||
@ -127,8 +129,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
|
||||
}
|
||||
|
||||
return (
|
||||
strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') * 2
|
||||
) < time();
|
||||
$this->get('ts_last_attempt') + $this->get('run_interval') * 2 * 1000
|
||||
) < DaemonUtil::timestampWithMilliseconds();
|
||||
}
|
||||
|
||||
public function hasBeenDisabled()
|
||||
@ -145,7 +147,9 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
|
||||
return $this->isWithinTimeperiod();
|
||||
}
|
||||
|
||||
if (strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') < time()) {
|
||||
if (
|
||||
$this->get('ts_last_attempt') + $this->get('run_interval') * 1000 < DaemonUtil::timestampWithMilliseconds()
|
||||
) {
|
||||
return $this->isWithinTimeperiod();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Web\Table;
|
||||
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
use Icinga\Module\Director\Daemon\DaemonUtil;
|
||||
|
||||
class JobTable extends ZfQueryBasedTable
|
||||
{
|
||||
@ -37,11 +38,11 @@ class JobTable extends ZfQueryBasedTable
|
||||
|
||||
protected function getJobClasses($row)
|
||||
{
|
||||
if ($row->unixts_last_attempt === null) {
|
||||
if ($row->ts_last_attempt === null) {
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
if ($row->unixts_last_attempt + $row->run_interval < time()) {
|
||||
if ($row->ts_last_attempt + $row->run_interval * 1000 < DaemonUtil::timestampWithMilliseconds()) {
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
@ -73,7 +74,6 @@ class JobTable extends ZfQueryBasedTable
|
||||
'run_interval' => 'j.run_interval',
|
||||
'last_attempt_succeeded' => 'j.last_attempt_succeeded',
|
||||
'ts_last_attempt' => 'j.ts_last_attempt',
|
||||
'unixts_last_attempt' => 'UNIX_TIMESTAMP(j.ts_last_attempt)',
|
||||
'ts_last_error' => 'j.ts_last_error',
|
||||
'last_error_message' => 'j.last_error_message',
|
||||
]
|
||||
|
@ -45,7 +45,7 @@ class JobDetails extends HtmlDocument
|
||||
|
||||
$tsLastAttempt = $job->get('ts_last_attempt');
|
||||
if ($tsLastAttempt) {
|
||||
$ts = \strtotime($tsLastAttempt);
|
||||
$ts = $tsLastAttempt / 1000;
|
||||
$timeAgo = Html::tag('span', [
|
||||
'class' => 'time-ago',
|
||||
'title' => DateFormatter::formatDateTime($ts)
|
||||
|
17
schema/mysql-migrations/upgrade_189.sql
Normal file
17
schema/mysql-migrations/upgrade_189.sql
Normal file
@ -0,0 +1,17 @@
|
||||
ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp BIGINT(20) DEFAULT NULL;
|
||||
ALTER TABLE director_job ADD COLUMN ts_last_error_tmp BIGINT(20) DEFAULT NULL;
|
||||
|
||||
|
||||
UPDATE director_job
|
||||
SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000,
|
||||
ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000;
|
||||
|
||||
ALTER TABLE director_job
|
||||
DROP COLUMN ts_last_attempt,
|
||||
DROP COLUMN ts_last_error,
|
||||
CHANGE ts_last_attempt_tmp ts_last_attempt BIGINT(20) DEFAULT NULL,
|
||||
CHANGE ts_last_error_tmp ts_last_error BIGINT(20) DEFAULT NULL;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (189, NOW());
|
@ -347,8 +347,8 @@ CREATE TABLE director_job (
|
||||
run_interval INT(10) UNSIGNED NOT NULL, -- seconds
|
||||
timeperiod_id INT(10) UNSIGNED DEFAULT NULL,
|
||||
last_attempt_succeeded ENUM('y', 'n') DEFAULT NULL,
|
||||
ts_last_attempt TIMESTAMP NULL DEFAULT NULL,
|
||||
ts_last_error TIMESTAMP NULL DEFAULT NULL,
|
||||
ts_last_attempt BIGINT(20) NULL DEFAULT NULL,
|
||||
ts_last_error BIGINT(20) NULL DEFAULT NULL,
|
||||
last_error_message TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (job_name),
|
||||
@ -2446,4 +2446,4 @@ CREATE TABLE branched_icinga_dependency (
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (188, NOW());
|
||||
VALUES (189, NOW());
|
||||
|
18
schema/pgsql-migrations/upgrade_189.sql
Normal file
18
schema/pgsql-migrations/upgrade_189.sql
Normal file
@ -0,0 +1,18 @@
|
||||
ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp bigint DEFAULT NULL;
|
||||
ALTER TABLE director_job ADD COLUMN ts_last_error_tmp bigint DEFAULT NULL;
|
||||
|
||||
|
||||
UPDATE director_job
|
||||
SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000,
|
||||
ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000;
|
||||
|
||||
ALTER TABLE director_job
|
||||
DROP COLUMN ts_last_attempt,
|
||||
DROP COLUMN ts_last_error;
|
||||
|
||||
ALTER TABLE director_job RENAME COLUMN ts_last_attempt_tmp TO ts_last_attempt;
|
||||
ALTER TABLE director_job RENAME COLUMN ts_last_error_tmp TO ts_last_error;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (189, NOW());
|
@ -448,8 +448,8 @@ CREATE TABLE director_job (
|
||||
run_interval integer NOT NULL, -- seconds
|
||||
timeperiod_id integer DEFAULT NULL,
|
||||
last_attempt_succeeded enum_boolean DEFAULT NULL,
|
||||
ts_last_attempt timestamp with time zone DEFAULT NULL,
|
||||
ts_last_error timestamp with time zone DEFAULT NULL,
|
||||
ts_last_attempt bigint DEFAULT NULL,
|
||||
ts_last_error bigint DEFAULT NULL,
|
||||
last_error_message text NULL DEFAULT NULL,
|
||||
CONSTRAINT director_job_period
|
||||
FOREIGN KEY (timeperiod_id)
|
||||
@ -2781,4 +2781,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (187, NOW());
|
||||
VALUES (189, NOW());
|
||||
|
Loading…
x
Reference in New Issue
Block a user