From 4d559757379d18d0746401b5a681e3f214448653 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Thu, 17 Nov 2016 10:15:58 +1300 Subject: [PATCH 01/28] Remove obsolete branch-alias --- composer.json | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index 87f012e0..96982fc4 100644 --- a/composer.json +++ b/composer.json @@ -1,24 +1,22 @@ { - "name": "silverstripe/queuedjobs", - "description": "A framework for defining and running background jobs in a queued manner", - "type": "silverstripe-module", - "keywords": ["silverstripe", "jobs"], - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Marcus Nyeholt", - "email": "marcus@silverstripe.com.au" - } - ], - "require": - { - "silverstripe/framework": "~3.1", - "silverstripe/multivaluefield": "~2.0", - "asyncphp/doorman": "~1.2" - }, - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev" - } - } -} + "name": "silverstripe/queuedjobs", + "description": "A framework for defining and running background jobs in a queued manner", + "type": "silverstripe-module", + "keywords": [ + "silverstripe", + "jobs" + ], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Marcus Nyeholt", + "email": "marcus@silverstripe.com.au" + } + ], + "require": { + "silverstripe/framework": "~3.1", + "silverstripe/multivaluefield": "~2.0", + "asyncphp/doorman": "~1.2" + }, + "extra": [] +} \ No newline at end of file From 3c49b8d83e910e2267b06450cc3d181f270ae691 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Thu, 17 Nov 2016 12:39:49 +1300 Subject: [PATCH 02/28] Update translations --- lang/ar.yml | 10 +++++----- lang/de.yml | 4 ++-- lang/en.yml | 2 ++ lang/eo.yml | 2 +- lang/fi.yml | 4 ++-- lang/hr.yml | 2 +- lang/mi.yml | 12 +++++------ lang/ru.yml | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lang/zh.yml | 22 ++++++++++----------- 9 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 lang/ru.yml diff --git a/lang/ar.yml b/lang/ar.yml index 55f3e634..271d6577 100644 --- a/lang/ar.yml +++ b/lang/ar.yml @@ -20,10 +20,10 @@ ar: MEMORY_RELEASE: 'ذاكرة إطلاق الوظائف و الانتظار (s% مستخدم)' STALLED_JOB: 'الوظيفة المؤجلة' STALLED_JOB_MSG: 'وظيفة باسم s% تبين أنها تعطلت. قد تم توقفها, من فضلك سجل الدخول لكى تتحقق منها' - TABLE_ADDE: تمت إضافته + TABLE_ADDE: 'تمت إضافته' TABLE_MESSAGES: رسالة - TABLE_NUM_PROCESSED: تم - TABLE_STARTED: تم البدأ + TABLE_NUM_PROCESSED: 'تم' + TABLE_STARTED: 'تم البدأ' TABLE_START_AFTER: 'ابدأ بعد' TABLE_STATUS: الحالة TABLE_TITLE: عنوان @@ -33,7 +33,7 @@ ar: ScheduledExecution: EXECUTE_EVERY: 'قم بتنفيذ كل' EXECUTE_FREE: 'محددة (بصيغة إس تى آر تو تايم من التنفيذ الأول)' - ExecuteEveryDay: يوم + ExecuteEveryDay: 'يوم' ExecuteEveryFortnight: أسبوعان ExecuteEveryHour: ساعة ExecuteEveryMonth: شهر @@ -41,6 +41,6 @@ ar: ExecuteEveryYear: سنة FIRST_EXECUTION: 'التنفيذ الأول' NEXT_RUN_DATE: 'موعد التشغيل التالى' - ScheduleTabTitle: الجدول الزمني + ScheduleTabTitle: 'الجدول الزمني' ScheduledExecutionJob: Title: 'موعد التنفيذ المحدد ل {title}' diff --git a/lang/de.yml b/lang/de.yml index 2932b108..d27be491 100644 --- a/lang/de.yml +++ b/lang/de.yml @@ -20,7 +20,7 @@ de: JOB_EXCEPT: 'Auftrag hat einen ''%s'' Fehler verursacht in %s Zeile %s ' JOB_PAUSED: 'Auftrag pausiert um %s' JOB_STALLED: 'Auftrag angehalten nach %s versuchen. Bitte überprüfen Sie den Auftrag.' - JOB_TYPE: 'Auftragstyp' + JOB_TYPE: Auftragstyp JobsFieldTitle: Aufträge STALLED_JOB: 'Angehaltener Auftrag' STALLED_JOB_MSG: 'Der Auftrag %s scheint festzustecken. Er wurde angehalten. Bitte loggen Sie sich ein, um den Auftrag zu überprüfen.' @@ -39,7 +39,7 @@ de: EXECUTE_EVERY: 'Alle ausführen' EXECUTE_FREE: 'Geplant (in strtotime-format nach dem ersten Ausführen)' ExecuteEveryDay: Tag - ExecuteEveryFortnight: Zwei Wochen + ExecuteEveryFortnight: 'Zwei Wochen' ExecuteEveryHour: Stunde ExecuteEveryMinute: Minute ExecuteEveryMonth: Monat diff --git a/lang/en.yml b/lang/en.yml index ddce50c7..757e10f5 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -1,4 +1,6 @@ en: + CleanupJob: + Title: 'Clean up old jobs from the database' CreateQueuedJobTask: Description: 'A task used to create a queued job. Pass the queued job class name as the "name" parameter, pass an optional "start" parameter (parseable by strtotime) to set a start time for the job.' DeleteObjectJob: diff --git a/lang/eo.yml b/lang/eo.yml index bbefe3fd..2d1d7f38 100644 --- a/lang/eo.yml +++ b/lang/eo.yml @@ -44,7 +44,7 @@ eo: EXECUTE_EVERY: 'Plenumi ĉiun' EXECUTE_FREE: 'Planita (en strtotime-formato ek de unua plenumo)' ExecuteEveryDay: Tago - ExecuteEveryFortnight: Du semajnoj + ExecuteEveryFortnight: 'Du semajnoj' ExecuteEveryHour: Horo ExecuteEveryMinute: Minuto ExecuteEveryMonth: Monato diff --git a/lang/fi.yml b/lang/fi.yml index ee30f75d..10ae4564 100644 --- a/lang/fi.yml +++ b/lang/fi.yml @@ -9,7 +9,7 @@ fi: SINGULARNAME: 'Jonossa olevan tehtävän kuvaaja' QueuedJobs: JOB_PAUSED: 'Tehtävä pysäytetty aikaan %s' - JOB_TYPE: 'Tehtävätyyppi' + JOB_TYPE: Tehtävätyyppi JobsFieldTitle: Tehtävät STALLED_JOB: 'Pysäytetty työ' STALLED_JOB_MSG: '%s tehtävä näyttää olevan seisahtunut. Se on nyt pysäytetty. Ole hyvä ja kirjaudu sisään tarkastellaksesi sitä' @@ -26,7 +26,7 @@ fi: ScheduledExecution: EXECUTE_EVERY: 'Suorita joka' ExecuteEveryDay: Päivä - ExecuteEveryFortnight: Kaksi viikkoa + ExecuteEveryFortnight: 'Kaksi viikkoa' ExecuteEveryHour: Tunti ExecuteEveryMonth: Kuukausi ExecuteEveryWeek: Viikko diff --git a/lang/hr.yml b/lang/hr.yml index b8861b92..28341c3c 100644 --- a/lang/hr.yml +++ b/lang/hr.yml @@ -31,7 +31,7 @@ hr: EXECUTE_EVERY: 'Izvrši svaki' EXECUTE_FREE: 'Planirano (u strtotime formatu od prvog pokretanja)' ExecuteEveryDay: dan - ExecuteEveryFortnight: dve nedjelje + ExecuteEveryFortnight: 'dve nedjelje' ExecuteEveryHour: sat ExecuteEveryMinute: minuta ExecuteEveryMonth: mjesec diff --git a/lang/mi.yml b/lang/mi.yml index 0ead6256..82552d7e 100644 --- a/lang/mi.yml +++ b/lang/mi.yml @@ -16,25 +16,25 @@ mi: JOB_PAUSED: 'i okioki te mahi i %s' JOB_STALLED: 'I auporoa te mahi i muri i ngā whakamātauranga %s - me tirotiro' JOB_TYPE: 'Momo mahi' - JobsFieldTitle: Ngā Mahi + JobsFieldTitle: 'Ngā Mahi' MEMORY_RELEASE: 'E tuku pūmahara ana te mahi, ka tataru (i whakamahia te %s)' STALLED_JOB: 'Mahi Kua Auporoa' STALLED_JOB_MSG: 'Te āhua nei, kua auporoa he mahi e kīia ana ko %s. Kua okioki, me takiuru anō kia tirohia' - TABLE_ADDE: I Tāpiritia + TABLE_ADDE: 'I Tāpiritia' TABLE_MESSAGES: Karere - TABLE_NUM_PROCESSED: Kua Oti - TABLE_STARTED: I Tīmata + TABLE_NUM_PROCESSED: 'Kua Oti' + TABLE_STARTED: 'I Tīmata' TABLE_START_AFTER: 'Tīmata Ā Muri' TABLE_STATUS: Tūnga TABLE_TITLE: Taitara TABLE_TOTAL: Tapeke QueuedJobsAdmin: - MENUTITLE: Ngā Mahi + MENUTITLE: 'Ngā Mahi' ScheduledExecution: EXECUTE_EVERY: 'Kawea Te Katoa' EXECUTE_FREE: 'Kua whakaritea ( i te hōputu strtotime mai i te kawenga tuatahi)' ExecuteEveryDay: Rā - ExecuteEveryFortnight: Rua Wiki + ExecuteEveryFortnight: 'Rua Wiki' ExecuteEveryHour: Haora ExecuteEveryMonth: Marama ExecuteEveryWeek: Wiki diff --git a/lang/ru.yml b/lang/ru.yml new file mode 100644 index 00000000..2e6176fa --- /dev/null +++ b/lang/ru.yml @@ -0,0 +1,57 @@ +ru: + CreateQueuedJobTask: + Description: 'Задача используется для создания отложенного действия. Укажите тип задачи, укажите опциональный параметр "start" (обрабатывается функцией strtotime) для времени начала.' + DeleteObjectJob: + DELETE_JOB: Удалить + DELETE_OBJ2: 'Удалить {title}' + GenerateSitemapJob: + REGENERATE: 'Генерировать Google sitemap .xml файл' + ProcessJobQueueTask: + Description: 'Используется cron для выполнения отложенных задач.' + PublishItemsJob: + Title: 'Опубликовать вложенные объекты {title}' + QueuedJobDescriptor: + PLURALNAME: 'Дескрипторы отложенных задач' + SINGULARNAME: 'Дескриптор отложенной задачи' + QueuedJobRule: + PLURALNAME: 'Настройки отложенных задач' + SINGULARNAME: 'Настройки отложенной задачи' + QueuedJobs: + CREATE_JOB_TYPE: 'Создать задачу типа' + CREATE_NEW_JOB: 'Создать задачу' + JOB_EXCEPT: 'Произошла ошибка задачи %s в %s на строчке %s' + JOB_PAUSED: 'Задача остановлена %s' + JOB_STALLED: 'Задача замороженна после %s попыток - пожалуйста проверьте' + JOB_TYPE: 'Тип задачи' + JOB_TYPE_PARAMS: 'Параметры конструктора задачи' + JobsFieldTitle: Задачи + MEMORY_RELEASE: 'Задача ожидает освобождения памяти (%s использовано)' + STALLED_JOB: 'Замороженная задача' + STALLED_JOB_MSG: 'Задача %s заморожена. Задача была остановлена, войдите в систему для проверки' + START_JOB_TIME: 'Начать задачу в' + TABLE_ADDE: Добавлена + TABLE_MESSAGES: Сообщение + TABLE_NUM_PROCESSED: Выполнено + TABLE_STARTED: Начата + TABLE_START_AFTER: 'Начать после' + TABLE_STATUS: Статус + TABLE_TITLE: Название + TABLE_TOTAL: Итого + TIME_LIMIT: 'Очередь достигла лимита по времени и будет начата заново перед продолжением' + QueuedJobsAdmin: + MENUTITLE: Задачи + ScheduledExecution: + EXECUTE_EVERY: 'Выполнять каждые' + EXECUTE_FREE: 'Запланировано (в формате strtotime после первого выполнения)' + ExecuteEveryDay: День + ExecuteEveryFortnight: 'Две недели' + ExecuteEveryHour: Час + ExecuteEveryMinute: Минута + ExecuteEveryMonth: Месяц + ExecuteEveryWeek: Неделя + ExecuteEveryYear: Год + FIRST_EXECUTION: 'Первое исполнение' + NEXT_RUN_DATE: 'Дата следующего исполнения' + ScheduleTabTitle: 'Расписание' + ScheduledExecutionJob: + Title: 'Запланировано исполнение {title}' diff --git a/lang/zh.yml b/lang/zh.yml index fae55252..1271a74d 100644 --- a/lang/zh.yml +++ b/lang/zh.yml @@ -9,29 +9,29 @@ zh: PublishItemsJob: Title: '发布下列项 {title}' QueuedJobDescriptor: - PLURALNAME: '排队作业描述符' - SINGULARNAME: '排队作业描述符' + PLURALNAME: 排队作业描述符 + SINGULARNAME: 排队作业描述符 QueuedJobs: JOB_EXCEPT: '作业导致例外 %s 位于 %s 的第 %s 行' JOB_PAUSED: '作业在 %s 暂停' JOB_STALLED: '%s 次尝试后作业停滞 —— 请检查' - JOB_TYPE: '工作类型' + JOB_TYPE: 工作类型 JobsFieldTitle: 作业 MEMORY_RELEASE: '工作正在释放内存并等待(%s 已用)' - STALLED_JOB: '呆滞任务' + STALLED_JOB: 呆滞任务 STALLED_JOB_MSG: '名为 %s 的工作似乎停滞不前。它已暂停,请登录查看' - TABLE_ADDE: 已添加 + TABLE_ADDE: '已添加' TABLE_MESSAGES: 消息 TABLE_NUM_PROCESSED: 完成 TABLE_STARTED: 已开始 - TABLE_START_AFTER: '开始于' + TABLE_START_AFTER: 开始于 TABLE_STATUS: 状态 - TABLE_TITLE: 标题 - TABLE_TOTAL: 全部 + TABLE_TITLE: '标题' + TABLE_TOTAL: '全部' QueuedJobsAdmin: MENUTITLE: 作业 ScheduledExecution: - EXECUTE_EVERY: '执行每' + EXECUTE_EVERY: 执行每 EXECUTE_FREE: '已调度(以首次执行的时间戳格式显示)' ExecuteEveryDay: 日 ExecuteEveryFortnight: 两周 @@ -39,8 +39,8 @@ zh: ExecuteEveryMonth: 月 ExecuteEveryWeek: 周 ExecuteEveryYear: 年 - FIRST_EXECUTION: '第一次执行' - NEXT_RUN_DATE: '下一次运行日期' + FIRST_EXECUTION: 第一次执行 + NEXT_RUN_DATE: 下一次运行日期 ScheduleTabTitle: 日程表 ScheduledExecutionJob: Title: '{title}计划执行' From d8cc024886bbc2eb9af35c552f700464a649342e Mon Sep 17 00:00:00 2001 From: Marcus Nyeholt Date: Fri, 13 Jan 2017 16:17:31 +1100 Subject: [PATCH 03/28] doc(composer) Removed 'master' branch alias for 2.x branch line --- composer.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composer.json b/composer.json index fe47d63b..8adb9a35 100644 --- a/composer.json +++ b/composer.json @@ -19,10 +19,5 @@ "silverstripe/framework": "~3.1", "silverstripe/multivaluefield": "~2.0", "asyncphp/doorman": "~1.2" - }, - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev" - } } } From 8794f26125fc92a65eeb46d491f94bfac4069114 Mon Sep 17 00:00:00 2001 From: Johannes Hammersen Date: Thu, 17 Nov 2016 16:50:29 +0100 Subject: [PATCH 04/28] Updates: Readme Added a note that the CleanupJob needs to be triggered manually once for it to start running every day. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf3b2a93..fb66a523 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ By default the CleanupJob is disabled. To enable it, set the following in your Y CleanupJob: is_enabled: true ``` -This will ensure that the CleanupJob is run once a day. +You will need to trigger the first run manually in the UI. After that the CleanupJob is run once a day. You can configure this job to clean up based on the number of jobs, or the age of the jobs. This is configured with the `cleanup_method` setting - current valid values are "age" (default) and "number". From e03ca86137a7730a0159ba4946a0402c8f049515 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Thu, 17 Nov 2016 15:20:35 +1300 Subject: [PATCH 05/28] Add self as author to composer.json --- composer.json | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 96982fc4..8adb9a35 100644 --- a/composer.json +++ b/composer.json @@ -1,22 +1,23 @@ { - "name": "silverstripe/queuedjobs", - "description": "A framework for defining and running background jobs in a queued manner", - "type": "silverstripe-module", - "keywords": [ - "silverstripe", - "jobs" - ], - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Marcus Nyeholt", - "email": "marcus@silverstripe.com.au" - } - ], - "require": { - "silverstripe/framework": "~3.1", - "silverstripe/multivaluefield": "~2.0", - "asyncphp/doorman": "~1.2" - }, - "extra": [] -} \ No newline at end of file + "name": "silverstripe/queuedjobs", + "description": "A framework for defining and running background jobs in a queued manner", + "type": "silverstripe-module", + "keywords": ["silverstripe", "jobs"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Marcus Nyeholt", + "email": "marcus@silverstripe.com.au" + }, + { + "name": "Damian Mooyman", + "email": "damian@silverstripe.com" + } + ], + "require": + { + "silverstripe/framework": "~3.1", + "silverstripe/multivaluefield": "~2.0", + "asyncphp/doorman": "~1.2" + } +} From 4072408ce6e597ea966ca2c025bd44b964fab53c Mon Sep 17 00:00:00 2001 From: Marcus Nyeholt Date: Tue, 7 Feb 2017 17:53:23 +1100 Subject: [PATCH 06/28] fix(QueuedJobService) Broken job status set Wait If a job breaks (exception or similar) its status should be set to "Broken", however if it _also_ exceeds memory or time limits, it _could_ be then set to "Wait" again, when it should remain broken. --- code/services/QueuedJobService.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index f0ac2f16..78d231c4 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -563,7 +563,11 @@ public function runJob($jobId) { _t('QueuedJobs.MEMORY_RELEASE', 'Job releasing memory and waiting (%s used)'), $this->humanReadable($this->getMemoryUsage()) )); - $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + + if ($jobDescriptor->JobStatus != QueuedJob::STATUS_BROKEN) { + $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + } + $broken = true; } @@ -573,7 +577,9 @@ public function runJob($jobId) { 'QueuedJobs.TIME_LIMIT', 'Queue has passed time limit and will restart before continuing' )); - $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + if ($jobDescriptor->JobStatus != QueuedJob::STATUS_BROKEN) { + $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + } $broken = true; } } From f17a1b9b13991cc032828543b2f4e85e0d84184e Mon Sep 17 00:00:00 2001 From: Marcus Nyeholt Date: Wed, 8 Feb 2017 10:05:09 +1100 Subject: [PATCH 07/28] test(QueuedJobService) Added testcase for memory expired broken job --- tests/QueuedJobsTest.php | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/QueuedJobsTest.php b/tests/QueuedJobsTest.php index 60e54045..3265c9ad 100644 --- a/tests/QueuedJobsTest.php +++ b/tests/QueuedJobsTest.php @@ -356,6 +356,26 @@ public function testJobHealthCheck() { $this->assertEquals(QueuedJob::STATUS_PAUSED, $descriptor->JobStatus); $this->assertEmpty($nextJob); } + + public function testExceptionWithMemoryExhaustion() { + $svc = $this->getService(); + $job = new TestExceptingJob(); + $job->firstJob = true; + $id = $svc->queueJob($job); + $descriptor = QueuedJobDescriptor::get()->byID($id); + + // we want to set the memory limit _really_ low so that our first run triggers + $mem = Config::inst()->get('QueuedJobService', 'memory_limit'); + Config::inst()->update('QueuedJobService', 'memory_limit', 1); + + $svc->runJob($id); + + Config::inst()->update('QueuedJobService', 'memory_limit', $mem); + + $descriptor = QueuedJobDescriptor::get()->byID($id); + + $this->assertEquals(QueuedJob::STATUS_BROKEN, $descriptor->JobStatus); + } } // stub class to be able to call init from an external context @@ -365,6 +385,31 @@ public function testInit($descriptor) { } } +class TestExceptingJob extends AbstractQueuedJob implements QueuedJob { + private $type = QueuedJob::QUEUED; + + public function __construct($type = null) { + $this->type = QueuedJob::IMMEDIATE; + $this->times = array(); + } + + public function getJobType() { + return $this->type; + } + + public function getTitle() { + return "A Test job throwing exceptions"; + } + + public function setup() { + $this->totalSteps = 1; + } + + public function process() { + throw new Exception("just excepted"); + } +} + class TestQueuedJob extends AbstractQueuedJob implements QueuedJob { private $type = QueuedJob::QUEUED; From d375eea361684a1e90a5e664e44c1fbdda8cf5a3 Mon Sep 17 00:00:00 2001 From: Mitchell Bennett Date: Mon, 13 Feb 2017 11:04:18 +1300 Subject: [PATCH 08/28] Solved issue #114 where a job's StartAfter date was always being saved in American format irrespective of the user's chosen DateFormat setting. --- code/controllers/QueuedJobsAdmin.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/controllers/QueuedJobsAdmin.php b/code/controllers/QueuedJobsAdmin.php index 937ff18f..dba636a8 100644 --- a/code/controllers/QueuedJobsAdmin.php +++ b/code/controllers/QueuedJobsAdmin.php @@ -38,6 +38,12 @@ class QueuedJobsAdmin extends ModelAdmin { 'EditForm' ); + /** + * European date format + * @var string + */ + private static $date_format_european = 'dd/MM/yyyy'; + /** * @var QueuedJobService */ @@ -134,6 +140,12 @@ public function createjob($data, Form $form) { $js = $form->Fields()->dataFieldByName('JobStart'); $time = $js->Value(); + // If the user has select the European date format as their setting then replace '/' with '-' in the date string so PHP + // treats the date as this format. + if (Member::currentUser()->DateFormat == self::$date_format_european) { + $time = str_replace('/', '-', $time); + } + if ($jobType && class_exists($jobType)) { $jobClass = new ReflectionClass($jobType); $job = $jobClass->newInstanceArgs($params); From 3dc2935ee99361ddea7420c5fdfc3b8b353fdfaa Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Tue, 14 Feb 2017 12:31:13 +1100 Subject: [PATCH 09/28] Add(queue) defaultJobs yml config for setting up a list of required jobs --- code/services/QueuedJobService.php | 58 +++++++++++++++++++++ templates/QueuedJobsDefaultJob.ss | 1 + tests/QueuedJobsTest.php | 83 ++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 templates/QueuedJobsDefaultJob.ss diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 78d231c4..531cd0bb 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -84,6 +84,12 @@ class QueuedJobService { */ public $queueRunner; + /** + * Config controlled list of default/required jobs + * @var Array + */ + public $defaultJobs; + /** * Register our shutdown handler */ @@ -314,6 +320,57 @@ public function checkJobHealth($queue = null) { } } + /** + * Checks through all the scheduled jobs that are expected to exist + */ + public function checkdefaultJobs($queue = null) { + $queue = $queue ?: QueuedJob::QUEUED; + if (count($this->defaultJobs)) { + + $activeJobs = QueuedJobDescriptor::get()->filter(array( + 'JobStatus' => array( + QueuedJob::STATUS_NEW, + QueuedJob::STATUS_INIT, + QueuedJob::STATUS_RUN, + QueuedJob::STATUS_WAIT, + ), + 'JobType' => $queue + )); + + foreach ($this->defaultJobs as $title => $jobConfig) { + if(count(array_intersect(array('filter', 'type'), array_keys($jobConfig))) !== 2) { + SS_Log::log("Default Job config: $title incorrectly set up. Please check the readme for examples", SS_Log::ERR); + continue; + } + + $job = $activeJobs->filter(array_merge( + array('Implementation' => $jobConfig['type']), $jobConfig['filter'] + )); + + if (!$job->count()) { + Email::create() + ->setTo(isset($jobConfig['email']) ? $jobConfig['email'] : Email::config()->admin_email) + ->setSubject('Default Job "' . $title . '" missing') + ->populateTemplate(array('title' => $title)) + ->populateTemplate($jobConfig) + ->setTemplate('QueuedJobsDefaultJob') + ->send(); + + if (isset($jobConfig['recreate']) && $jobConfig['recreate']) { + if (count(array_intersect(array('construct', 'startDateFormat', 'startTimeString'), array_keys($jobConfig))) !== 3) { + SS_Log::log("Default Job config: $title incorrectly set up. Please check the readme for examples", SS_Log::ERR); + continue; + } + singleton('QueuedJobService')->queueJob( + Injector::inst()->createWithArgs($jobConfig['type'], $jobConfig['construct']), + date($jobConfig['startDateFormat'], strtotime($jobConfig['startTimeString'])) + ); + } + } + } + } + } + /** * Attempt to restart a stalled job * @@ -785,6 +842,7 @@ public function getJobListFilter($type = null, $includeUpUntil = 0) { */ public function runQueue($queue) { $this->checkJobHealth($queue); + $this->checkdefaultJobs($queue); $this->queueRunner->runQueue($queue); } diff --git a/templates/QueuedJobsDefaultJob.ss b/templates/QueuedJobsDefaultJob.ss new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/templates/QueuedJobsDefaultJob.ss @@ -0,0 +1 @@ + diff --git a/tests/QueuedJobsTest.php b/tests/QueuedJobsTest.php index 3265c9ad..2085910d 100644 --- a/tests/QueuedJobsTest.php +++ b/tests/QueuedJobsTest.php @@ -376,6 +376,89 @@ public function testExceptionWithMemoryExhaustion() { $this->assertEquals(QueuedJob::STATUS_BROKEN, $descriptor->JobStatus); } + + public function testCheckdefaultJobs() { + // Create a job and add it to the queue + $svc = $this->getService(); + $testDefaultJobsArray = array( + 'ArbitraryName' => array( + # I'll get restarted and create an alert email + 'type' => 'TestQueuedJob', + 'filter' => array( + 'JobTitle' => "A Test job" + ), + 'recreate' => 1, + 'construct' => array( + 'queue' => QueuedJob::QUEUED + ), + 'startDateFormat' => 'Y-m-d 02:00:00', + 'startTimeString' => 'tomorrow', + 'email' => 'test@queuejobtest.com' + )); + $svc->defaultJobs = $testDefaultJobsArray; + $jobConfig = $testDefaultJobsArray['ArbitraryName']; + + $activeJobs = QueuedJobDescriptor::get()->filter( + 'JobStatus', array( + QueuedJob::STATUS_NEW, + QueuedJob::STATUS_INIT, + QueuedJob::STATUS_RUN, + QueuedJob::STATUS_WAIT, + ) + ); + //assert no jobs currently active + $this->assertEquals(0, $activeJobs->count()); + + //add a default job to the queue + $svc->checkdefaultJobs(); + $this->assertEquals(1, $activeJobs->count()); + $descriptor = $activeJobs->filter(array_merge( + array('Implementation' => $jobConfig['type']), $jobConfig['filter'] + ))->first(); + // Verify initial state is new + $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus); + + //update Job to broken + $descriptor->JobStatus = QueuedJob::STATUS_BROKEN; + $descriptor->write(); + //check and add job for broken job + $svc->checkdefaultJobs(); + $this->assertEquals(1, $activeJobs->count()); + //assert we now have 2 of our job (one good one broken) + $this->assertEquals(2, QueuedJobDescriptor::get()->count()); + + //test not adding a job when job is there already + $svc->checkdefaultJobs(); + $this->assertEquals(1, $activeJobs->count()); + //assert we now have 2 of our job (one good one broken) + $this->assertEquals(2, QueuedJobDescriptor::get()->count()); + + //test add jobs with various start dates + $job = $activeJobs->first(); + date('Y-m-d 02:00:00', strtotime('+1 day')); + $this->assertEquals(date('Y-m-d 02:00:00', strtotime('+1 day')), $job->StartAfter); + //swap start time to midday + $testDefaultJobsArray['ArbitraryName']['startDateFormat'] = 'Y-m-d 12:00:00'; + //clean up then add new jobs + $svc->defaultJobs = $testDefaultJobsArray; + $activeJobs->removeAll(); + $svc->checkdefaultJobs(); + //assert one jobs currently active + $this->assertEquals(1, $activeJobs->count()); + $job = $activeJobs->first(); + $this->assertEquals(date('Y-m-d 12:00:00', strtotime('+1 day')), $job->StartAfter); + //test alert email + $email = $this->findEmail('test@queuejobtest.com'); + $this->assertNotNull($email); + + //test broken job config + unset($testDefaultJobsArray['ArbitraryName']['startDateFormat']); + //clean up then add new jobs + $svc->defaultJobs = $testDefaultJobsArray; + $activeJobs->removeAll(); + $svc->checkdefaultJobs(); + } + } // stub class to be able to call init from an external context From b3e40dc91ee02cbea42e12b0041579f18588ce76 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Tue, 14 Feb 2017 14:34:14 +1100 Subject: [PATCH 10/28] Fix(defaultJobs) add content to missing job email --- code/services/QueuedJobService.php | 2 +- templates/QueuedJobsDefaultJob.ss | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 531cd0bb..330b0adb 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -351,7 +351,7 @@ public function checkdefaultJobs($queue = null) { Email::create() ->setTo(isset($jobConfig['email']) ? $jobConfig['email'] : Email::config()->admin_email) ->setSubject('Default Job "' . $title . '" missing') - ->populateTemplate(array('title' => $title)) + ->populateTemplate(array('Title' => $title, 'Site' => Director::absoluteBaseURL())) ->populateTemplate($jobConfig) ->setTemplate('QueuedJobsDefaultJob') ->send(); diff --git a/templates/QueuedJobsDefaultJob.ss b/templates/QueuedJobsDefaultJob.ss index 8b137891..6e79e5db 100644 --- a/templates/QueuedJobsDefaultJob.ss +++ b/templates/QueuedJobsDefaultJob.ss @@ -1 +1,8 @@ +Hi, +$Title job not found on $Site +type: $type +Start Time: $startDateFormat +Start Day: $startTimeString + +Log in to $Site to see further details and take any necessary actions. \ No newline at end of file From 0215e7009826d3f52b0f80ca98d4b89adf6655b1 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Mon, 20 Feb 2017 10:57:33 +1100 Subject: [PATCH 11/28] Fix(defaultJobs) improve code clarity --- code/services/QueuedJobService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 330b0adb..b68bd1d3 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -338,7 +338,7 @@ public function checkdefaultJobs($queue = null) { )); foreach ($this->defaultJobs as $title => $jobConfig) { - if(count(array_intersect(array('filter', 'type'), array_keys($jobConfig))) !== 2) { + if (!isset($jobConfig['filter']) || !isset($jobConfig['type'])) { SS_Log::log("Default Job config: $title incorrectly set up. Please check the readme for examples", SS_Log::ERR); continue; } @@ -357,7 +357,7 @@ public function checkdefaultJobs($queue = null) { ->send(); if (isset($jobConfig['recreate']) && $jobConfig['recreate']) { - if (count(array_intersect(array('construct', 'startDateFormat', 'startTimeString'), array_keys($jobConfig))) !== 3) { + if (!isset($jobConfig['construct']) || !isset($jobConfig['startDateFormat']) || !isset($jobConfig['startTimeString'])) { SS_Log::log("Default Job config: $title incorrectly set up. Please check the readme for examples", SS_Log::ERR); continue; } From f9ee41f8888a61c4099cd788114a63966d28e851 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Fri, 31 Mar 2017 15:53:41 +1100 Subject: [PATCH 12/28] Add check for status paused to default job check --- code/services/QueuedJobService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index b68bd1d3..28514c88 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -333,6 +333,7 @@ public function checkdefaultJobs($queue = null) { QueuedJob::STATUS_INIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_WAIT, + QueuedJob::STATUS_PAUSED ), 'JobType' => $queue )); From 5da00e616312f19a99b32eca1586a11f6309168e Mon Sep 17 00:00:00 2001 From: Michael van Schaik Date: Sat, 1 Apr 2017 06:47:03 +0200 Subject: [PATCH 13/28] Making inclusion-duration of finished jobs configurable --- code/controllers/QueuedJobsAdmin.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/controllers/QueuedJobsAdmin.php b/code/controllers/QueuedJobsAdmin.php index dba636a8..849f1e98 100644 --- a/code/controllers/QueuedJobsAdmin.php +++ b/code/controllers/QueuedJobsAdmin.php @@ -48,6 +48,12 @@ class QueuedJobsAdmin extends ModelAdmin { * @var QueuedJobService */ public $jobQueue; + + /** + * @config The number of seconds to include jobs that have finished + * default: 300 (5 minutes), examples: 3600(1h), 86400(1d) + */ + private static $max_finished_jobs_age = 300; /** * @param int $id @@ -57,7 +63,7 @@ class QueuedJobsAdmin extends ModelAdmin { public function getEditForm($id = null, $fields = null) { $form = parent::getEditForm($id, $fields); - $filter = $this->jobQueue->getJobListFilter(null, 300); + $filter = $this->jobQueue->getJobListFilter(null, self::config()->max_finished_jobs_age); $list = DataList::create('QueuedJobDescriptor'); $list = $list->where($filter)->sort('Created', 'DESC'); From 842712fba2933a6e1a4340e240d88c31e83eb1f4 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Mon, 3 Apr 2017 10:59:51 +1000 Subject: [PATCH 14/28] Add test for status paused default jobs --- tests/QueuedJobsTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/QueuedJobsTest.php b/tests/QueuedJobsTest.php index 2085910d..dd5dde76 100644 --- a/tests/QueuedJobsTest.php +++ b/tests/QueuedJobsTest.php @@ -404,6 +404,7 @@ public function testCheckdefaultJobs() { QueuedJob::STATUS_INIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_WAIT, + QueuedJob::STATUS_PAUSED ) ); //assert no jobs currently active @@ -418,6 +419,15 @@ public function testCheckdefaultJobs() { // Verify initial state is new $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus); + //update Job to paused + $descriptor->JobStatus = QueuedJob::STATUS_PAUSED; + $descriptor->write(); + //check defaults the paused job shoudl be ignored + $svc->checkdefaultJobs(); + $this->assertEquals(1, $activeJobs->count()); + //assert we now still have 1 of our job (paused) + $this->assertEquals(1, QueuedJobDescriptor::get()->count()); + //update Job to broken $descriptor->JobStatus = QueuedJob::STATUS_BROKEN; $descriptor->write(); From 218017e83df9b7cc7f60e4fde12449611d80d771 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Mon, 3 Apr 2017 15:31:34 +1000 Subject: [PATCH 15/28] Update readme to explain default jobs --- README.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/README.md b/README.md index fb66a523..a887332b 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,82 @@ Note - if you do NOT have this running, make sure to set `QueuedJobService::$use so that immediate mode jobs don't stall. By setting this to true, immediate jobs will be executed after the request finishes as the php script ends. +# Default Jobs + +Some jobs should always be either running or queued to run, things like data refreshes or periodic clean up jobs, we call these Default Jobs. +Default jobs are checked for at the end of each job queue process, using the job type and any fields in the filter to create an SQL query e.g. + +``` +ArbitraryName: + type: 'ScheduledExternalImportJob' + filter: + JobTitle: 'Scheduled import from Services' +``` + +Will become: + +``` +QueuedJobDescriptor::get()->filter(array( + 'type' => 'ScheduledExternalImportJob', + 'JobTitle' => 'Scheduled import from Services' +)); +``` + +This query is checked to see if there's at least 1 healthly (new, run, wait or paused) job matching the filter. If there's not and recreate is true in the yml config we use the construct array as params to pass to a new job object e.g: + +``` +ArbitraryName: + type: 'ScheduledExternalImportJob' + filter: + JobTitle: 'Scheduled import from Services' + recreate: 1 + construct: + repeat: 300 + contentItem: 100 + target: 157 +``` +If the above job is missing it will be recreated as: +``` +Injector::inst()->createWithArgs('ScheduledExternalImportJob', $construct[]) +``` + +### Pausing Default Jobs + +If you need to stop a default job from raising alerts and being recreated, set an existing copy of the job to Paused in the CMS. + +### YML config + +Default jobs are defined in yml config the sample below covers the options and expected values + +``` +Injector: + QueuedJobService: + defaultJobs: + # This key is used as the title for error logs and alert emails + ArbitraryName: + # The job type should be the class name of a job REQUIRED + type: 'ScheduledExternalImportJob' + # This plus the job type is used to create the SQL query REQUIRED + filter: + # 1 or more Fieldname: 'value' sets that will be queried on REQUIRED + # These can be valid ORM filter + JobTitle: 'Scheduled import from Services' + # Sets whether the job will be recreated or not OPTIONAL + recreate: 1 + # Set the email address to send the alert to if not set site admin email is used OPTIONAL + email: 'admin@email.com' + # Parameters set on the recreated object OPTIONAL + construct: + # 1 or more Fieldname: 'value' sets be passed to the constructor OPTIONAL + repeat: 300 + title: 'Scheduled import from Services' + # Minimal implementation will send alerts but not recreate + AnotherTitle: + type: 'AJob' + filter: + JobTitle: 'A job' +``` + ## Configuring the CleanupJob By default the CleanupJob is disabled. To enable it, set the following in your YML: From 3f094b35c194cd7e0499108e460a3bc8003c4af6 Mon Sep 17 00:00:00 2001 From: Marcus Nyeholt Date: Mon, 8 May 2017 09:44:35 +1000 Subject: [PATCH 16/28] FIX Issue where setting isComplete=true during 'setup()' or 'prepareForRestart()' causes the job to say its "Running" indefinitely. (Commit on behalf of Jake) --- code/services/QueuedJobService.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 28514c88..38dbc60c 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -551,8 +551,13 @@ public function runJob($jobId) { $jobDescriptor->JobRestarted = date('Y-m-d H:i:s'); } - $jobDescriptor->JobStatus = QueuedJob::STATUS_RUN; - $jobDescriptor->write(); + + // Only write to job as "Running" if 'isComplete' was NOT set to true + // during setup() or prepareForRestart() + if (!$job->jobFinished()) { + $jobDescriptor->JobStatus = QueuedJob::STATUS_RUN; + $jobDescriptor->write(); + } $lastStepProcessed = 0; // have we stalled at all? From a28aae9c5234c241447d7752d6464d7042c3b4be Mon Sep 17 00:00:00 2001 From: Jake Bentvelzen Date: Wed, 10 May 2017 13:18:33 +1000 Subject: [PATCH 17/28] fix(JobErrorHandler): Fix bug where deprecation / variable set in if-statement would always cause zero outputting of errors to console. Modified logic to align with Core silverstripe functions --- code/services/QueuedJobService.php | 55 +++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index f0ac2f16..d621b606 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -540,7 +540,7 @@ public function runJob($jobId) { } catch (Exception $e) { // okay, we'll just catch this exception for now $job->addMessage(sprintf(_t('QueuedJobs.JOB_EXCEPT', 'Job caused exception %s in %s at line %s'), $e->getMessage(), $e->getFile(), $e->getLine()), 'ERROR'); - SS_Log::log($e, SS_Log::ERR); + $errorHandler->handleException($e); $jobDescriptor->JobStatus = QueuedJob::STATUS_BROKEN; } @@ -604,7 +604,7 @@ public function runJob($jobId) { } } catch (Exception $e) { // okay, we'll just catch this exception for now - SS_Log::log($e, SS_Log::ERR); + $errorHandler->handleException($e); $jobDescriptor->JobStatus = QueuedJob::STATUS_BROKEN; $jobDescriptor->write(); $broken = true; @@ -844,25 +844,56 @@ public function clear() { restore_error_handler(); } + /** + * For logging and catching exceptions thrown during AbstractQueuedJob::process() + * and similar. + */ + public function handleException($exception) { + $errno = E_USER_ERROR; + $type = get_class($exception); + $message = "Uncaught " . $type . ": " . $exception->getMessage(); + $file = $exception->getFile(); + $line = $exception->getLine(); + $context = $exception->getTrace(); + + // NOTE: This will call SS_Log::log() + Debug::fatalHandler($errno, $message, $file, $line, $context); + } + + /** + * Works like the core Silverstripe error handler without exiting + * on fatal messages. + */ public function handleError($errno, $errstr, $errfile, $errline) { if (error_reporting()) { // Don't throw E_DEPRECATED in PHP 5.3+ - if (defined('E_DEPRECATED')) { - if ($errno == E_DEPRECATED || $errno = E_USER_DEPRECATED) { + /*if (defined('E_DEPRECATED')) { + if ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) { return; } - } + }*/ switch ($errno) { case E_NOTICE: case E_USER_NOTICE: - case E_STRICT: { - break; - } - default: { - throw new Exception($errstr . " in $errfile at line $errline", $errno); - break; - } + case E_DEPRECATED: + case E_USER_DEPRECATED: + case E_STRICT: + Debug::noticeHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); + break; + + case E_WARNING: + case E_CORE_WARNING: + case E_USER_WARNING: + case E_RECOVERABLE_ERROR: + Debug::warningHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); + break; + + default: + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + // Old exception throw + //throw new Exception($errstr . " in $errfile at line $errline", $errno); + break; } } } From 2720a9868660b74a0523dc79ae53920de1b21b24 Mon Sep 17 00:00:00 2001 From: Fred Condo Date: Mon, 22 May 2017 13:50:11 -0700 Subject: [PATCH 18/28] Always enqueue a new CleanupJob when complete Before this patch, if there were no jobs to clean up, no new CleanupJob was scheduled because of a premature return in process(). --- code/jobs/CleanupJob.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/jobs/CleanupJob.php b/code/jobs/CleanupJob.php index d4bee708..2aa86d26 100644 --- a/code/jobs/CleanupJob.php +++ b/code/jobs/CleanupJob.php @@ -127,6 +127,7 @@ public function process() { if (empty($staleJobs)) { $this->addMessage("No jobs to clean up."); $this->isComplete = true; + $this->reenqueue(); return; } $numJobs = count($staleJobs); @@ -136,13 +137,18 @@ public function process() { IN (\'' . $staleJobs . '\')' ); $this->addMessage($numJobs . " jobs cleaned up."); - // let's make sure there is a cleanupJob in the queue + $this->reenqueue(); + $this->isComplete = true; + return; + } + + private function reenqueue() + { if (Config::inst()->get('CleanupJob', 'is_enabled')) { - $this->addMessage("Queueing the next Cleanup Job."); + $this->addMessage("Queueing the next Cleanup Job."); $cleanup = new CleanupJob(); singleton('QueuedJobService')->queueJob($cleanup, date('Y-m-d H:i:s', time() + 86400)); } - $this->isComplete = true; - return; } + } From a98e02f38d518c9d21c2bc118cb20b4c1bf86225 Mon Sep 17 00:00:00 2001 From: Nathan Glasl Date: Fri, 30 Jun 2017 18:17:03 +1000 Subject: [PATCH 19/28] FIX, correcting an issue where the module would end up on the wrong path. --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8adb9a35..bc5886cb 100644 --- a/composer.json +++ b/composer.json @@ -19,5 +19,8 @@ "silverstripe/framework": "~3.1", "silverstripe/multivaluefield": "~2.0", "asyncphp/doorman": "~1.2" - } + }, + "extra": { + "installer-name": "queuedjobs" + } } From b0a83fb934e7dc2f01144920dcbcf12da1cfd311 Mon Sep 17 00:00:00 2001 From: Nathan Glasl Date: Fri, 30 Jun 2017 18:17:03 +1000 Subject: [PATCH 20/28] FIX, correcting an issue where the module would end up on the wrong path. --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8adb9a35..bc5886cb 100644 --- a/composer.json +++ b/composer.json @@ -19,5 +19,8 @@ "silverstripe/framework": "~3.1", "silverstripe/multivaluefield": "~2.0", "asyncphp/doorman": "~1.2" - } + }, + "extra": { + "installer-name": "queuedjobs" + } } From 4262e039f55030aaf3b0c5e23723574a583ed2ed Mon Sep 17 00:00:00 2001 From: Michael van Schaik Date: Sat, 1 Apr 2017 06:47:03 +0200 Subject: [PATCH 21/28] Making inclusion-duration of finished jobs configurable --- code/controllers/QueuedJobsAdmin.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/controllers/QueuedJobsAdmin.php b/code/controllers/QueuedJobsAdmin.php index dba636a8..849f1e98 100644 --- a/code/controllers/QueuedJobsAdmin.php +++ b/code/controllers/QueuedJobsAdmin.php @@ -48,6 +48,12 @@ class QueuedJobsAdmin extends ModelAdmin { * @var QueuedJobService */ public $jobQueue; + + /** + * @config The number of seconds to include jobs that have finished + * default: 300 (5 minutes), examples: 3600(1h), 86400(1d) + */ + private static $max_finished_jobs_age = 300; /** * @param int $id @@ -57,7 +63,7 @@ class QueuedJobsAdmin extends ModelAdmin { public function getEditForm($id = null, $fields = null) { $form = parent::getEditForm($id, $fields); - $filter = $this->jobQueue->getJobListFilter(null, 300); + $filter = $this->jobQueue->getJobListFilter(null, self::config()->max_finished_jobs_age); $list = DataList::create('QueuedJobDescriptor'); $list = $list->where($filter)->sort('Created', 'DESC'); From a667ff383faab709c3fa7be0d381cc4c3b91172e Mon Sep 17 00:00:00 2001 From: Jono Menz Date: Mon, 3 Jul 2017 11:14:04 -0700 Subject: [PATCH 22/28] Vendor update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bc5886cb..6d88589f 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "require": { "silverstripe/framework": "~3.1", - "silverstripe/multivaluefield": "~2.0", + "symbiote/silverstripe-multivaluefield": "~2.0", "asyncphp/doorman": "~1.2" }, "extra": { From 308d0a7e747775d528bf899beacc5ae10d14d1ec Mon Sep 17 00:00:00 2001 From: Jake Bentvelzen Date: Wed, 5 Jul 2017 11:12:05 +1000 Subject: [PATCH 23/28] fix(QueuedJobService): When a job hits the "Job releasing memory and waiting" case and completed successfully, it would not run the 'afterComplete' logic. --- code/services/QueuedJobService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 6a26f7ce..7dc734e6 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -667,7 +667,7 @@ public function runJob($jobId) { $jobDescriptor->write(); } - if (!$broken) { + if ($job->jobFinished()) { $job->afterComplete(); $jobDescriptor->cleanupJob(); } From 59f0cb90ab1b3c3eceb537196220246070f1581b Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Wed, 5 Jul 2017 18:05:53 +1000 Subject: [PATCH 24/28] Fix(defaultJobs) config now loads correctly. Add SS_Log of missing job. Change admin email queued_job_admin_email --- code/services/QueuedJobService.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 28514c88..1f54cf1a 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -88,7 +88,7 @@ class QueuedJobService { * Config controlled list of default/required jobs * @var Array */ - public $defaultJobs; + public $defaultJobs = array(); /** * Register our shutdown handler @@ -325,6 +325,9 @@ public function checkJobHealth($queue = null) { */ public function checkdefaultJobs($queue = null) { $queue = $queue ?: QueuedJob::QUEUED; + + $this->defaultJobs = !empty($this->defaultJobs) ? $this->defaultJobs : Config::inst()->get(__CLASS__, 'defaultJobs'); + if (count($this->defaultJobs)) { $activeJobs = QueuedJobDescriptor::get()->filter(array( @@ -349,8 +352,10 @@ public function checkdefaultJobs($queue = null) { )); if (!$job->count()) { + SS_Log::log("Default Job config: $title was missing from Queue and has been re-added", SS_Log::ERR); Email::create() - ->setTo(isset($jobConfig['email']) ? $jobConfig['email'] : Email::config()->admin_email) + ->setTo(isset($jobConfig['email']) ? $jobConfig['email'] : Config::inst()->get('Email', 'queued_job_admin_email')) + ->setFrom(Config::inst()->get('Email', 'queued_job_admin_email')) ->setSubject('Default Job "' . $title . '" missing') ->populateTemplate(array('Title' => $title, 'Site' => Director::absoluteBaseURL())) ->populateTemplate($jobConfig) @@ -358,7 +363,7 @@ public function checkdefaultJobs($queue = null) { ->send(); if (isset($jobConfig['recreate']) && $jobConfig['recreate']) { - if (!isset($jobConfig['construct']) || !isset($jobConfig['startDateFormat']) || !isset($jobConfig['startTimeString'])) { + if (!array_key_exists('construct', $jobConfig) || !isset($jobConfig['startDateFormat']) || !isset($jobConfig['startTimeString'])) { SS_Log::log("Default Job config: $title incorrectly set up. Please check the readme for examples", SS_Log::ERR); continue; } From 35d1ade3303c1eda4292366f8995d73f278be967 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Wed, 19 Jul 2017 10:27:51 +1000 Subject: [PATCH 25/28] Fix(defaultJobs) update readme and default jobs check to use injector correctly --- README.md | 49 +++++++++++++++--------------- code/services/QueuedJobService.php | 2 -- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a887332b..69a19932 100644 --- a/README.md +++ b/README.md @@ -172,30 +172,31 @@ Default jobs are defined in yml config the sample below covers the options and e ``` Injector: QueuedJobService: - defaultJobs: - # This key is used as the title for error logs and alert emails - ArbitraryName: - # The job type should be the class name of a job REQUIRED - type: 'ScheduledExternalImportJob' - # This plus the job type is used to create the SQL query REQUIRED - filter: - # 1 or more Fieldname: 'value' sets that will be queried on REQUIRED - # These can be valid ORM filter - JobTitle: 'Scheduled import from Services' - # Sets whether the job will be recreated or not OPTIONAL - recreate: 1 - # Set the email address to send the alert to if not set site admin email is used OPTIONAL - email: 'admin@email.com' - # Parameters set on the recreated object OPTIONAL - construct: - # 1 or more Fieldname: 'value' sets be passed to the constructor OPTIONAL - repeat: 300 - title: 'Scheduled import from Services' - # Minimal implementation will send alerts but not recreate - AnotherTitle: - type: 'AJob' - filter: - JobTitle: 'A job' + properties: + defaultJobs: + # This key is used as the title for error logs and alert emails + ArbitraryName: + # The job type should be the class name of a job REQUIRED + type: 'ScheduledExternalImportJob' + # This plus the job type is used to create the SQL query REQUIRED + filter: + # 1 or more Fieldname: 'value' sets that will be queried on REQUIRED + # These can be valid ORM filter + JobTitle: 'Scheduled import from Services' + # Sets whether the job will be recreated or not OPTIONAL + recreate: 1 + # Set the email address to send the alert to if not set site admin email is used OPTIONAL + email: 'admin@email.com' + # Parameters set on the recreated object OPTIONAL + construct: + # 1 or more Fieldname: 'value' sets be passed to the constructor OPTIONAL + repeat: 300 + title: 'Scheduled import from Services' + # Minimal implementation will send alerts but not recreate + AnotherTitle: + type: 'AJob' + filter: + JobTitle: 'A job' ``` ## Configuring the CleanupJob diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index 1f54cf1a..04ae53b5 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -326,8 +326,6 @@ public function checkJobHealth($queue = null) { public function checkdefaultJobs($queue = null) { $queue = $queue ?: QueuedJob::QUEUED; - $this->defaultJobs = !empty($this->defaultJobs) ? $this->defaultJobs : Config::inst()->get(__CLASS__, 'defaultJobs'); - if (count($this->defaultJobs)) { $activeJobs = QueuedJobDescriptor::get()->filter(array( From 4103227be50726a4ec031bfad95a171c0d8864c4 Mon Sep 17 00:00:00 2001 From: Stephen McMahon Date: Thu, 20 Jul 2017 14:24:28 +1000 Subject: [PATCH 26/28] Fix(defaultJobs) fix filtering of active jobs. Improve log messages --- code/services/QueuedJobService.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index e8d22534..ad9629e6 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -328,16 +328,15 @@ public function checkdefaultJobs($queue = null) { if (count($this->defaultJobs)) { - $activeJobs = QueuedJobDescriptor::get()->filter(array( - 'JobStatus' => array( + $activeJobs = QueuedJobDescriptor::get()->filter( + 'JobStatus', array( QueuedJob::STATUS_NEW, QueuedJob::STATUS_INIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_WAIT, QueuedJob::STATUS_PAUSED - ), - 'JobType' => $queue - )); + ) + ); foreach ($this->defaultJobs as $title => $jobConfig) { if (!isset($jobConfig['filter']) || !isset($jobConfig['type'])) { @@ -350,7 +349,7 @@ public function checkdefaultJobs($queue = null) { )); if (!$job->count()) { - SS_Log::log("Default Job config: $title was missing from Queue and has been re-added", SS_Log::ERR); + SS_Log::log("Default Job config: $title was missing from Queue", SS_Log::ERR); Email::create() ->setTo(isset($jobConfig['email']) ? $jobConfig['email'] : Config::inst()->get('Email', 'queued_job_admin_email')) ->setFrom(Config::inst()->get('Email', 'queued_job_admin_email')) @@ -369,6 +368,7 @@ public function checkdefaultJobs($queue = null) { Injector::inst()->createWithArgs($jobConfig['type'], $jobConfig['construct']), date($jobConfig['startDateFormat'], strtotime($jobConfig['startTimeString'])) ); + SS_Log::log("Default Job config: $title has been re-added to the Queue", SS_Log::ERR); } } } From 0f5928c925db4174fdd3743deaf3a1a55962b6b8 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Mon, 28 Aug 2017 15:02:54 +1200 Subject: [PATCH 27/28] Re-vendor framework 3.x compatible version as 3.0.x-dev --- LICENSE | 18 +++---- README.md | 42 ++++++++-------- code/QJUtils.php | 2 +- code/controllers/QueuedJobsAdmin.php | 8 +-- code/dataobjects/QueuedJobDescriptor.php | 2 +- .../ScheduledExecutionExtension.php | 2 +- code/jobs/CleanupJob.php | 2 +- code/jobs/DeleteObjectJob.php | 2 +- code/jobs/GenerateGoogleSitemapJob.php | 2 +- code/jobs/PublishItemsJob.php | 2 +- code/jobs/ScheduledExecutionJob.php | 2 +- code/services/AbstractQueuedJob.php | 6 +-- code/services/DefaultQueueHandler.php | 2 +- code/services/GearmanQueueHandler.php | 2 +- code/services/ImmediateQueueHandler.php | 2 +- code/services/QueuedJob.php | 2 +- code/services/QueuedJobService.php | 6 +-- code/tasks/CreateQueuedJobTask.php | 2 +- code/tasks/ProcessJobQueueTask.php | 2 +- code/tasks/PublishItemsTask.php | 2 +- code/workers/JobWorker.php | 2 +- composer.json | 50 +++++++++++-------- tests/QueuedJobsTest.php | 2 +- tests/ScheduledExecutionTest.php | 2 +- 24 files changed, 86 insertions(+), 80 deletions(-) diff --git a/LICENSE b/LICENSE index c4487962..e2ebba70 100644 --- a/LICENSE +++ b/LICENSE @@ -1,17 +1,17 @@ -Copyright (c) 2009, SilverStripe Australia PTY LTD - www.silverstripe.com.au +Copyright (c) 2009, Symbiote PTY LTD - www.symbiote.com.au All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of SilverStripe nor the names of its contributors may be used to endorse or promote products derived from this software + * Neither the name of SilverStripe nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. diff --git a/README.md b/README.md index 69a19932..b87c73e6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # SilverStripe Queued Jobs Module -[![Build Status](https://travis-ci.org/silverstripe-australia/silverstripe-queuedjobs.svg?branch=master)](https://travis-ci.org/silverstripe-australia/silverstripe-queuedjobs) -[![Scrutinizer](https://scrutinizer-ci.com/g/silverstripe-australia/silverstripe-queuedjobs/badges/quality-score.png)](https://scrutinizer-ci.com/g/silverstripe-australia/silverstripe-queuedjobs/) +[![Build Status](https://travis-ci.org/symbiote/silverstripe-queuedjobs.svg?branch=master)](https://travis-ci.org/symbiote/silverstripe-queuedjobs) +[![Scrutinizer](https://scrutinizer-ci.com/g/symbiote/silverstripe-queuedjobs/badges/quality-score.png)](https://scrutinizer-ci.com/g/symbiote/silverstripe-queuedjobs/) ## Maintainer Contact Marcus Nyeholt - + ## Requirements @@ -21,12 +21,12 @@ Marcus Nyeholt The master branch of this module is currently aiming for SilverStripe 3.1 compatibility -* [SilverStripe 3.0 compatible version](https://github.com/silverstripe-australia/silverstripe-queuedjobs/tree/1.0) -* [SilverStripe 2.4 compatible version](https://github.com/silverstripe-australia/silverstripe-queuedjobs/tree/ss24) +* [SilverStripe 3.0 compatible version](https://github.com/symbiote/silverstripe-queuedjobs/tree/1.0) +* [SilverStripe 2.4 compatible version](https://github.com/symbiote/silverstripe-queuedjobs/tree/ss24) ## Documentation -See http://github.com/silverstripe-australia/silverstripe-queuedjobs/wiki/ for more complete +See http://github.com/symbiote/silverstripe-queuedjobs/wiki/ for more complete documentation The Queued Jobs module provides a framework for SilverStripe developers to @@ -60,7 +60,7 @@ that processes this queue. Its time of execution can be left a little longer. singleton('QueuedJobService')->queueJob($publish); * To schedule a job to be executed at some point in the future, pass a date through with the call to queueJob -The following will run the publish job in 1 day's time from now. +The following will run the publish job in 1 day's time from now. $publish = new PublishItemsJob(21); singleton('QueuedJobService')->queueJob($publish, date('Y-m-d H:i:s', time() + 86400)); @@ -84,7 +84,7 @@ After: '#queuedjobsettings' --- Injector: QueuedJobService: - properties: + properties: queueRunner: %$DoormanRunner ``` @@ -101,7 +101,7 @@ Name: localproject After: '#queuedjobsettings' --- Injector: - QueueHandler: + QueueHandler: class: GearmanQueueHandler ``` @@ -116,15 +116,15 @@ Queued jobs can be executed immediately (instead of being limited by cron's 1 mi a file based notification system. This relies on something like inotifywait to monitor a folder (by default this is SILVERSTRIPE_CACHE_DIR/queuedjobs) and triggering the ProcessJobQueueTask as above but passing job=$filename as the argument. An example script is in queuedjobs/scripts that will run -inotifywait and then call the ProcessJobQueueTask when a new job is ready to run. +inotifywait and then call the ProcessJobQueueTask when a new job is ready to run. Note - if you do NOT have this running, make sure to set `QueuedJobService::$use_shutdown_function = true;` so that immediate mode jobs don't stall. By setting this to true, immediate jobs will be executed after -the request finishes as the php script ends. +the request finishes as the php script ends. # Default Jobs -Some jobs should always be either running or queued to run, things like data refreshes or periodic clean up jobs, we call these Default Jobs. +Some jobs should always be either running or queued to run, things like data refreshes or periodic clean up jobs, we call these Default Jobs. Default jobs are checked for at the end of each job queue process, using the job type and any fields in the filter to create an SQL query e.g. ``` @@ -195,7 +195,7 @@ Injector: # Minimal implementation will send alerts but not recreate AnotherTitle: type: 'AJob' - filter: + filter: JobTitle: 'A job' ``` @@ -227,7 +227,7 @@ CleanupJob: cleanup_statuses: - Broken - Complete -``` +``` ## Troubleshooting @@ -236,15 +236,15 @@ To make sure your job works, you can first try to execute the job directly outsi queues - this can be done by manually calling the *setup()* and *process()* methods. If it works fine under these circumstances, try having *getJobType()* return *QueuedJob::IMMEDIATE* to have execution work immediately, without being persisted or executed via cron. If this works, next make sure your -cronjob is configured and executing correctly. +cronjob is configured and executing correctly. If defining your own job classes, be aware that when the job is started on the queue, the job class is constructed _without_ parameters being passed; this means if you accept constructor args, you -_must_ detect whether they're present or not before using them. See [this issue](https://github.com/silverstripe-australia/silverstripe-queuedjobs/issues/35) -and [this wiki page](https://github.com/silverstripe-australia/silverstripe-queuedjobs/wiki/Defining-queued-jobs) for +_must_ detect whether they're present or not before using them. See [this issue](https://github.com/symbiote/silverstripe-queuedjobs/issues/35) +and [this wiki page](https://github.com/symbiote/silverstripe-queuedjobs/wiki/Defining-queued-jobs) for more information -Ensure that notifications are configured so that you can get updates or stalled or broken jobs. You can +Ensure that notifications are configured so that you can get updates or stalled or broken jobs. You can set the notification email address in your config as below: @@ -254,9 +254,9 @@ set the notification email address in your config as below: **Long running jobs are running multiple times!** -A long running job _may_ fool the system into thinking it has gone away (ie the job health check fails because +A long running job _may_ fool the system into thinking it has gone away (ie the job health check fails because `currentStep` hasn't been incremented). To avoid this scenario, you can set `$this->currentStep = -1` in your job's -constructor, to prevent any health checks detecting the job. +constructor, to prevent any health checks detecting the job. ## Performance configuration @@ -284,7 +284,7 @@ resources. By default this is disabled, so you must specify this in your project ## Indexes -ALTER TABLE `QueuedJobDescriptor` ADD INDEX ( `JobStatus` , `JobType` ) +ALTER TABLE `QueuedJobDescriptor` ADD INDEX ( `JobStatus` , `JobType` ) ## Contributing diff --git a/code/QJUtils.php b/code/QJUtils.php index 11c037ea..0ac53471 100644 --- a/code/QJUtils.php +++ b/code/QJUtils.php @@ -4,7 +4,7 @@ * A set of utility functions used by the queued jobs module * * @license http://silverstripe.org/bsd-license - * @author Marcus Nyeholt + * @author Marcus Nyeholt */ class QJUtils { public function __construct() {} diff --git a/code/controllers/QueuedJobsAdmin.php b/code/controllers/QueuedJobsAdmin.php index 849f1e98..bf3f8581 100644 --- a/code/controllers/QueuedJobsAdmin.php +++ b/code/controllers/QueuedJobsAdmin.php @@ -1,6 +1,6 @@ + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class QueuedJobsAdmin extends ModelAdmin { @@ -39,7 +39,7 @@ class QueuedJobsAdmin extends ModelAdmin { ); /** - * European date format + * European date format * @var string */ private static $date_format_european = 'dd/MM/yyyy'; @@ -48,7 +48,7 @@ class QueuedJobsAdmin extends ModelAdmin { * @var QueuedJobService */ public $jobQueue; - + /** * @config The number of seconds to include jobs that have finished * default: 300 (5 minutes), examples: 3600(1h), 86400(1d) @@ -119,7 +119,7 @@ public function getEditForm($id = null, $fields = null) { $actions = $form->Actions(); $actions->push(FormAction::create('createjob', _t('QueuedJobs.CREATE_NEW_JOB', 'Create new job'))); } - + $this->extend('updateEditForm', $form); return $form; diff --git a/code/dataobjects/QueuedJobDescriptor.php b/code/dataobjects/QueuedJobDescriptor.php index 6a53bc5a..ea36a75d 100644 --- a/code/dataobjects/QueuedJobDescriptor.php +++ b/code/dataobjects/QueuedJobDescriptor.php @@ -25,7 +25,7 @@ * * @method Member RunAs() Member to run this job as * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class QueuedJobDescriptor extends DataObject { diff --git a/code/extensions/ScheduledExecutionExtension.php b/code/extensions/ScheduledExecutionExtension.php index 80fbbfa5..87e2fda3 100644 --- a/code/extensions/ScheduledExecutionExtension.php +++ b/code/extensions/ScheduledExecutionExtension.php @@ -6,7 +6,7 @@ * * Developers who want to use these capabilities can set up * - * @author marcus@silverstripe.com.au + * @author marcus@symbiote.com.au * @license BSD License http://silverstripe.org/bsd-license/ */ class ScheduledExecutionExtension extends DataExtension { diff --git a/code/jobs/CleanupJob.php b/code/jobs/CleanupJob.php index 2aa86d26..67f6fa2f 100644 --- a/code/jobs/CleanupJob.php +++ b/code/jobs/CleanupJob.php @@ -4,7 +4,7 @@ * An queued job to clean out the QueuedJobDescriptor Table * which often gets too full * - * @author Andrew Aitken-Fincham + * @author Andrew Aitken-Fincham */ class CleanupJob extends AbstractQueuedJob implements QueuedJob { diff --git a/code/jobs/DeleteObjectJob.php b/code/jobs/DeleteObjectJob.php index 65414524..47c057f1 100644 --- a/code/jobs/DeleteObjectJob.php +++ b/code/jobs/DeleteObjectJob.php @@ -5,7 +5,7 @@ * a schedule, or where the delete may have some onflow affect that takes a while to * finish the deletion. * - * @author marcus@silverstripe.com.au + * @author marcus@symbiote.com.au * @license BSD License http://silverstripe.org/bsd-license/ */ class DeleteObjectJob extends AbstractQueuedJob { diff --git a/code/jobs/GenerateGoogleSitemapJob.php b/code/jobs/GenerateGoogleSitemapJob.php index 9014ee38..0380fd3a 100644 --- a/code/jobs/GenerateGoogleSitemapJob.php +++ b/code/jobs/GenerateGoogleSitemapJob.php @@ -5,7 +5,7 @@ * * If the sitemap module is installed, uses information from that to populate things * - * @author marcus@silverstripe.com.au + * @author marcus@symbiote.com.au * @license http://silverstripe.org/bsd-license/ */ class GenerateGoogleSitemapJob extends AbstractQueuedJob { diff --git a/code/jobs/PublishItemsJob.php b/code/jobs/PublishItemsJob.php index c07cfadb..15eac67d 100644 --- a/code/jobs/PublishItemsJob.php +++ b/code/jobs/PublishItemsJob.php @@ -5,7 +5,7 @@ * * Use this as an example of how you can write your own jobs * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class PublishItemsJob extends AbstractQueuedJob implements QueuedJob { diff --git a/code/jobs/ScheduledExecutionJob.php b/code/jobs/ScheduledExecutionJob.php index 8272630c..97b364bc 100644 --- a/code/jobs/ScheduledExecutionJob.php +++ b/code/jobs/ScheduledExecutionJob.php @@ -5,7 +5,7 @@ * it will call the onScheduledExecution method on the owning * dataobject. * - * @author marcus@silverstripe.com.au + * @author marcus@symbiote.com.au * @license BSD License http://silverstripe.org/bsd-license/ */ class ScheduledExecutionJob extends AbstractQueuedJob { diff --git a/code/services/AbstractQueuedJob.php b/code/services/AbstractQueuedJob.php index 7d08cb39..93d0724a 100644 --- a/code/services/AbstractQueuedJob.php +++ b/code/services/AbstractQueuedJob.php @@ -7,7 +7,7 @@ * jobData with relevant variables needed to process() your job later on in execution. If you do not, * please ensure you do before you queueJob() the job, to ensure the signature that is generated is 'correct'. * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ abstract class AbstractQueuedJob implements QueuedJob { @@ -35,14 +35,14 @@ abstract class AbstractQueuedJob implements QueuedJob { * @var boolean */ protected $isComplete = false; - + /** * Extensions can have a construct but don't have too. * Without a construct, it's impossible to create a job in the CMS * @var array params */ public function __construct($params = array()) { - + } /** diff --git a/code/services/DefaultQueueHandler.php b/code/services/DefaultQueueHandler.php index 4bbee351..c33d86a7 100644 --- a/code/services/DefaultQueueHandler.php +++ b/code/services/DefaultQueueHandler.php @@ -3,7 +3,7 @@ /** * Default method for handling items run via the cron * - * @author marcus@silverstripe.com.au + * @author marcus@symbiote.com.au * @license BSD License http://silverstripe.org/bsd-license/ */ class DefaultQueueHandler { diff --git a/code/services/GearmanQueueHandler.php b/code/services/GearmanQueueHandler.php index b92ca871..b8288a7a 100644 --- a/code/services/GearmanQueueHandler.php +++ b/code/services/GearmanQueueHandler.php @@ -1,6 +1,6 @@ + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ interface QueuedJob { diff --git a/code/services/QueuedJobService.php b/code/services/QueuedJobService.php index ad9629e6..e33ca17f 100644 --- a/code/services/QueuedJobService.php +++ b/code/services/QueuedJobService.php @@ -18,7 +18,7 @@ * data->write(); * * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class QueuedJobService { @@ -920,7 +920,7 @@ public function clear() { /** * For logging and catching exceptions thrown during AbstractQueuedJob::process() * and similar. - */ + */ public function handleException($exception) { $errno = E_USER_ERROR; $type = get_class($exception); @@ -936,7 +936,7 @@ public function handleException($exception) { /** * Works like the core Silverstripe error handler without exiting * on fatal messages. - */ + */ public function handleError($errno, $errstr, $errfile, $errline) { if (error_reporting()) { // Don't throw E_DEPRECATED in PHP 5.3+ diff --git a/code/tasks/CreateQueuedJobTask.php b/code/tasks/CreateQueuedJobTask.php index 91ea115e..ca486c86 100644 --- a/code/tasks/CreateQueuedJobTask.php +++ b/code/tasks/CreateQueuedJobTask.php @@ -8,7 +8,7 @@ * If no name is given, it creates a demo dummy job to help test that things * are set up and working * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class CreateQueuedJobTask extends BuildTask { diff --git a/code/tasks/ProcessJobQueueTask.php b/code/tasks/ProcessJobQueueTask.php index acffe0bd..01a138ca 100644 --- a/code/tasks/ProcessJobQueueTask.php +++ b/code/tasks/ProcessJobQueueTask.php @@ -3,7 +3,7 @@ /** * Task used to process the job queue * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class ProcessJobQueueTask extends BuildTask { diff --git a/code/tasks/PublishItemsTask.php b/code/tasks/PublishItemsTask.php index 38f94159..57a410d3 100644 --- a/code/tasks/PublishItemsTask.php +++ b/code/tasks/PublishItemsTask.php @@ -4,7 +4,7 @@ * An example build task that publishes a bunch of pages - this demonstrates a realworld example of how the * queued jobs project can be used * - * @author Marcus Nyeholt + * @author Marcus Nyeholt * @license BSD http://silverstripe.org/bsd-license/ */ class PublishItemsTask extends BuildTask { diff --git a/code/workers/JobWorker.php b/code/workers/JobWorker.php index 2534e35a..06bbdb6d 100644 --- a/code/workers/JobWorker.php +++ b/code/workers/JobWorker.php @@ -1,7 +1,7 @@ + * @author Marcus Nyeholt */ class QueuedJobsTest extends SapphireTest { diff --git a/tests/ScheduledExecutionTest.php b/tests/ScheduledExecutionTest.php index ebb0926c..27ee42b3 100644 --- a/tests/ScheduledExecutionTest.php +++ b/tests/ScheduledExecutionTest.php @@ -1,7 +1,7 @@ Date: Thu, 2 Nov 2017 14:54:08 +0000 Subject: [PATCH 28/28] FIX ImmediateQueueHandler needs `scheduleJob` method to match expected API --- code/services/ImmediateQueueHandler.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/services/ImmediateQueueHandler.php b/code/services/ImmediateQueueHandler.php index 572e6541..2961c2af 100644 --- a/code/services/ImmediateQueueHandler.php +++ b/code/services/ImmediateQueueHandler.php @@ -25,4 +25,8 @@ class ImmediateQueueHandler { public function startJobOnQueue(QueuedJobDescriptor $job) { $this->queuedJobService->runJob($job->ID); } + + public function scheduleJob(QueuedJobDescriptor $job, $date) { + $this->queuedJobService->runJob($job->ID); + } }