diff --git a/README.md b/README.md index c9b6a4a6..cdffd30a 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,83 @@ 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: + 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 By default the CleanupJob is disabled. To enable it, set the following in your 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/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}计划执行' diff --git a/src/Jobs/CleanupJob.php b/src/Jobs/CleanupJob.php index 19f63178..82ff274e 100644 --- a/src/Jobs/CleanupJob.php +++ b/src/Jobs/CleanupJob.php @@ -140,6 +140,7 @@ public function process() if (empty($staleJobs)) { $this->addMessage("No jobs to clean up."); $this->isComplete = true; + $this->reenqueue(); return; } $numJobs = count($staleJobs); @@ -149,13 +150,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('Symbiote\\QueuedJobs\\Jobs\\CleanupJob', 'is_enabled')) { $this->addMessage("Queueing the next Cleanup Job."); $cleanup = new CleanupJob(); singleton('Symbiote\\QueuedJobs\\Services\\QueuedJobService') ->queueJob($cleanup, date('Y-m-d H:i:s', time() + 86400)); } - $this->isComplete = true; - return; } } diff --git a/src/Services/ImmediateQueueHandler.php b/src/Services/ImmediateQueueHandler.php index 41c43f6c..9ac921a9 100644 --- a/src/Services/ImmediateQueueHandler.php +++ b/src/Services/ImmediateQueueHandler.php @@ -31,4 +31,12 @@ public function startJobOnQueue(QueuedJobDescriptor $job) { $this->queuedJobService->runJob($job->ID); } + + /** + * @param QueuedJobDescriptor $job + */ + public function scheduleJob(QueuedJobDescriptor $job, $date) + { + $this->queuedJobService->runJob($job->ID); + } } diff --git a/src/Services/QueuedJobService.php b/src/Services/QueuedJobService.php index 3e2bea93..d14b0fe1 100644 --- a/src/Services/QueuedJobService.php +++ b/src/Services/QueuedJobService.php @@ -115,6 +115,12 @@ class QueuedJobService */ public $queueRunner; + /** + * Config controlled list of default/required jobs + * @var array + */ + public $defaultJobs = []; + /** * Register our shutdown handler */ @@ -357,6 +363,61 @@ public function checkJobHealth($queue = null) } } + /** + * Checks through ll 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( + 'JobStatus', + array( + QueuedJob::STATUS_NEW, + QueuedJob::STATUS_INIT, + QueuedJob::STATUS_RUN, + QueuedJob::STATUS_WAIT, + QueuedJob::STATUS_PAUSED, + ) + ); + foreach ($this->defaultJobs as $title => $jobConfig) { + if (!isset($jobConfig['filter']) || !isset($jobConfig['type'])) { + $this->getLogger()->error("Default Job config: $title incorrectly set up. Please check the readme for examples"); + continue; + } + $job = $activeJobs->filter(array_merge( + array('Implementation' => $jobConfig['type']), + $jobConfig['filter'] + )); + if (!$job->count()) { + $this->getLogger()->error("Default Job config: $title was missing from Queue"); + 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')) + ->setSubject('Default Job "' . $title . '" missing') + ->populateTemplate([ + 'Title' => $title, + 'Site' => Director::absoluteBaseURL(), + ]) + ->populateTemplate($jobConfig) + ->setTemplate('QueuedJobsDefaultJob') + ->send(); + if (isset($jobConfig['recreate']) && $jobConfig['recreate']) { + if (!array_key_exists('construct', $jobConfig) || !isset($jobConfig['startDateFormat']) || !isset($jobConfig['startTimeString'])) { + $this->getLogger()->error("Default Job config: $title incorrectly set up. Please check the readme for examples"); + continue; + } + singleton('QueuedJobService')->queueJob( + Injector::inst()->createWithArgs($jobConfig['type'], $jobConfig['construct']), + date($jobConfig['startDateFormat'], strtotime($jobConfig['startTimeString'])) + ); + $this->getLogger()->error("Default Job config: $title has been re-added to the Queue"); + } + } + } + } + } + /** * Attempt to restart a stalled job * @@ -649,7 +710,9 @@ public function runJob($jobId) ['used' => $this->humanReadable($this->getMemoryUsage())] ) ); - $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + if ($jobDescriptor->JobStatus != QueuedJob::STATUS_BROKEN) { + $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; + } $broken = true; } @@ -659,7 +722,9 @@ public function runJob($jobId) __CLASS__ . '.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; } } @@ -888,6 +953,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..6e79e5db --- /dev/null +++ b/templates/QueuedJobsDefaultJob.ss @@ -0,0 +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 diff --git a/tests/QueuedJobsTest.php b/tests/QueuedJobsTest.php index 5a9a809f..4178bffa 100644 --- a/tests/QueuedJobsTest.php +++ b/tests/QueuedJobsTest.php @@ -9,6 +9,7 @@ use Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor; use Symbiote\QueuedJobs\Services\QueuedJob; use Symbiote\QueuedJobs\Services\QueuedJobService; +use Symbiote\QueuedJobs\Tests\QueuedJobsTest\TestExceptingJob; use Symbiote\QueuedJobs\Tests\QueuedJobsTest\TestQueuedJob; use Symbiote\QueuedJobs\Tests\QueuedJobsTest\TestQJService; @@ -381,4 +382,120 @@ public function testJobHealthCheck() $this->assertEmpty($nextJob); $this->assertContains('A job named A Test job appears to have stalled. It has been paused, please login to check it', $logger->getMessages()); } + + 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); + } + + 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, + QueuedJob::STATUS_PAUSED, + ) + ); + //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 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(); + //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(); + } } diff --git a/tests/QueuedJobsTest/TestExceptingJob.php b/tests/QueuedJobsTest/TestExceptingJob.php new file mode 100644 index 00000000..16c094ee --- /dev/null +++ b/tests/QueuedJobsTest/TestExceptingJob.php @@ -0,0 +1,38 @@ +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"); + } +}