From 27976f56846ad317ea39f481633d0d9893bc70da Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sat, 9 Feb 2019 14:44:28 +0100 Subject: [PATCH 01/10] Save process id when job starts --- Plugin/Cron/Model/ScheduleResourcePlugin.php | 56 +++++++++++++++++++ Test/Integration/ProcessIdTest.php | 58 ++++++++++++++++++++ etc/crontab/di.xml | 3 + etc/db_schema.xml | 7 +++ 4 files changed, 124 insertions(+) create mode 100644 Plugin/Cron/Model/ScheduleResourcePlugin.php create mode 100644 Test/Integration/ProcessIdTest.php create mode 100644 etc/db_schema.xml diff --git a/Plugin/Cron/Model/ScheduleResourcePlugin.php b/Plugin/Cron/Model/ScheduleResourcePlugin.php new file mode 100644 index 00000000..a268aa1d --- /dev/null +++ b/Plugin/Cron/Model/ScheduleResourcePlugin.php @@ -0,0 +1,56 @@ +getConnection(); + + // this condition added to avoid cron jobs locking after incorrect termination of running job + $match = $connection->quoteInto( + 'existing.job_code = current.job_code ' . + 'AND (existing.executed_at > UTC_TIMESTAMP() - INTERVAL 1 DAY OR existing.executed_at IS NULL) ' . + 'AND existing.status = ?', + $newStatus + ); + + $selectIfUnlocked = $connection->select() + ->joinLeft( + ['existing' => $subject->getTable('cron_schedule')], + $match, + [ + 'status' => new \Zend_Db_Expr($connection->quote($newStatus)), + 'pid' => new \Zend_Db_Expr($connection->quote(\getmypid())) + ] + ) + ->where('current.schedule_id = ?', $scheduleId) + ->where('current.status = ?', $currentStatus) + ->where('existing.schedule_id IS NULL'); + + $update = $connection->updateFromSelect($selectIfUnlocked, ['current' => $subject->getTable('cron_schedule')]); + $result = $connection->query($update)->rowCount(); + + if ($result == 1) { + return true; + } + return false; } +} \ No newline at end of file diff --git a/Test/Integration/ProcessIdTest.php b/Test/Integration/ProcessIdTest.php new file mode 100644 index 00000000..6f79bcb8 --- /dev/null +++ b/Test/Integration/ProcessIdTest.php @@ -0,0 +1,58 @@ +objectManager = Bootstrap::getObjectManager(); + } + public function testProcessIdSavedOnStart() + { + $this->givenPid($pid); + $this->givenPendingSchedule($schedule); + $this->whenTryLockJob($schedule); + $this->thenScheduleIsSavedWithPid($schedule, $pid); + } + + private function givenPendingSchedule(&$schedule) + { + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_PENDING); + $schedule->save(); + } + + private function whenTryLockJob(Schedule $schedule) + { + $schedule->tryLockJob(); + } + + private function thenScheduleIsSavedWithPid(Schedule $schedule, $pid) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($pid, $schedule->getData('pid'), 'PID should be saved in schedule'); + } + + private function givenPid(&$pid): void + { + $pid = \getmypid(); + $this->assertNotFalse($pid, 'Precondition: getmypid() should not return false'); + } +} \ No newline at end of file diff --git a/etc/crontab/di.xml b/etc/crontab/di.xml index 079ec836..00fadda2 100755 --- a/etc/crontab/di.xml +++ b/etc/crontab/di.xml @@ -3,4 +3,7 @@ + + + diff --git a/etc/db_schema.xml b/etc/db_schema.xml new file mode 100644 index 00000000..4a5fea9f --- /dev/null +++ b/etc/db_schema.xml @@ -0,0 +1,7 @@ + + + + +
+
From 5240ea15be34abec531f386346f62fb68d5b1775 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sat, 9 Feb 2019 14:54:30 +0100 Subject: [PATCH 02/10] Add PID column to grid --- Test/Integration/ProcessIdTest.php | 2 +- .../ui_component/cronjobmanager_manage_grid.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Test/Integration/ProcessIdTest.php b/Test/Integration/ProcessIdTest.php index 6f79bcb8..333af7a1 100644 --- a/Test/Integration/ProcessIdTest.php +++ b/Test/Integration/ProcessIdTest.php @@ -55,4 +55,4 @@ private function givenPid(&$pid): void $pid = \getmypid(); $this->assertNotFalse($pid, 'Precondition: getmypid() should not return false'); } -} \ No newline at end of file +} diff --git a/view/adminhtml/ui_component/cronjobmanager_manage_grid.xml b/view/adminhtml/ui_component/cronjobmanager_manage_grid.xml index b029f9a5..d32d81cc 100755 --- a/view/adminhtml/ui_component/cronjobmanager_manage_grid.xml +++ b/view/adminhtml/ui_component/cronjobmanager_manage_grid.xml @@ -210,6 +210,16 @@ + + + + text + true + PID + false + + + From 01cad228fc443090f9e7f402f5e8ad07fc218892 Mon Sep 17 00:00:00 2001 From: Thore Baden Date: Sat, 9 Feb 2019 17:28:37 +0000 Subject: [PATCH 03/10] [ADD] process id in timeline template --- view/adminhtml/web/template/timeline/timeline.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/view/adminhtml/web/template/timeline/timeline.html b/view/adminhtml/web/template/timeline/timeline.html index 5e56a1d0..49c35f85 100644 --- a/view/adminhtml/web/template/timeline/timeline.html +++ b/view/adminhtml/web/template/timeline/timeline.html @@ -83,6 +83,12 @@
+
+ : +
+
+ +
:
From d248128b6239979ce1452c7ece51159e0d6b3f5b Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sat, 9 Feb 2019 17:16:11 +0100 Subject: [PATCH 04/10] Remove previous implementation of PID setting The new implementation saves a round trip to the database and saves the pid together with the status "running" Also for Magento 2.3+, db_schema.xml is used now --- Plugin/Cron/Model/SchedulePlugin.php | 38 ---------------------------- Setup/UpgradeSchema.php | 16 ++++++++++++ etc/di.xml | 3 --- 3 files changed, 16 insertions(+), 41 deletions(-) delete mode 100644 Plugin/Cron/Model/SchedulePlugin.php diff --git a/Plugin/Cron/Model/SchedulePlugin.php b/Plugin/Cron/Model/SchedulePlugin.php deleted file mode 100644 index 1fa630d8..00000000 --- a/Plugin/Cron/Model/SchedulePlugin.php +++ /dev/null @@ -1,38 +0,0 @@ -resourceModel = $resourceModel; - } - - /** - * If the return from @see \Magento\Cron\Model\Schedule::tryLockJob is - * true, the job has started in THIS process, if it returns false, it has - * not started, probably because it was already running. - * - * @param \Magento\Cron\Model\Schedule $subject - * @param $return - * @return boolean - * @throws \Magento\Framework\Exception\AlreadyExistsException - */ - public function afterTryLockJob(Schedule $subject, $return) - { - if ($return) { - $subject->setData("pid", getmypid()); - $this->resourceModel->save($subject); // Save A.S.A.P, in case the process is killed - } - - return $return; - } -} \ No newline at end of file diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 45edfdd0..56de31bb 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -16,6 +16,16 @@ class UpgradeSchema implements UpgradeSchemaInterface /** @var ModuleContextInterface */ private $context; + /** + * @var \Magento\Framework\App\ProductMetadataInterface + */ + private $magentoMetaData; + + public function __construct(\Magento\Framework\App\ProductMetadataInterface $magentoMetaData) + { + $this->magentoMetaData = $magentoMetaData; + } + /** * {@inheritdoc} */ @@ -40,6 +50,12 @@ public function upgrade( */ public function addPidToSchedule() { + if (version_compare($this->magentoMetaData->getVersion(), '2.3.0', '>=')) { + /* + * For Magento 2.3+, db_schema.xml is used instead + */ + return; + } $this->setup->getConnection()->addColumn( $this->setup->getTable("cron_schedule"), "pid", diff --git a/etc/di.xml b/etc/di.xml index 8f54d2a7..c7be1959 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -27,7 +27,4 @@
- - - From fffa25ff9d865ea0bc04b4bd73f1c153d4b4b5af Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sat, 9 Feb 2019 20:23:23 +0100 Subject: [PATCH 05/10] Add UI for kill requests Kill requests are saved but not handled yet --- Api/ScheduleManagementInterface.php | 6 ++ Controller/Adminhtml/Manage/Job/Kill.php | 52 ++++++++++++ Model/ScheduleManagement.php | 21 +++++ Observer/ProcessKillRequestsObserver.php | 19 +++++ Setup/UpgradeSchema.php | 28 +++++++ .../Model/ScheduleManagementTest.php | 84 +++++++++++++++++++ Ui/Component/Listing/Column/CronActions.php | 21 ++++- etc/crontab/events.xml | 1 + etc/db_schema.xml | 1 + etc/module.xml | 2 +- etc/webapi.xml | 6 ++ 11 files changed, 238 insertions(+), 3 deletions(-) create mode 100755 Controller/Adminhtml/Manage/Job/Kill.php create mode 100644 Observer/ProcessKillRequestsObserver.php create mode 100644 Test/Integration/Model/ScheduleManagementTest.php diff --git a/Api/ScheduleManagementInterface.php b/Api/ScheduleManagementInterface.php index 9811087e..fb6ee6c3 100644 --- a/Api/ScheduleManagementInterface.php +++ b/Api/ScheduleManagementInterface.php @@ -54,4 +54,10 @@ public function schedule(string $jobCode, int $time): Schedule; * @return bool */ public function flush(): bool; + + /** + * @param int $jobId + * @return bool + */ + public function kill(int $jobId, int $timestamp): bool; } diff --git a/Controller/Adminhtml/Manage/Job/Kill.php b/Controller/Adminhtml/Manage/Job/Kill.php new file mode 100755 index 00000000..bfca3de4 --- /dev/null +++ b/Controller/Adminhtml/Manage/Job/Kill.php @@ -0,0 +1,52 @@ +scheduleManagement = $scheduleManagement; + } + + /** + * Save cronjob + * + * @return Void + */ + public function execute() + { + $jobId = (int)$this->getRequest()->getParam('id'); + $jobCode = $this->getRequest()->getParam('job_code'); + try { + if ($this->scheduleManagement->kill($jobId, \time())) { + $this->getMessageManager()->addSuccessMessage("Job will be killed by next cron run: {$jobCode}"); + } else { + $this->getMessageManager()->addNoticeMessage("Job cannot be killed."); + } + } catch (\Exception $e) { + $this->getMessageManager()->addErrorMessage($e->getMessage()); + $this->_redirect('*/manage/index/'); + return; + } + $this->_redirect('*/manage/index/'); + } +} diff --git a/Model/ScheduleManagement.php b/Model/ScheduleManagement.php index 92f4bb3f..ca425e6c 100644 --- a/Model/ScheduleManagement.php +++ b/Model/ScheduleManagement.php @@ -121,4 +121,25 @@ public function flush(): bool return true; } + + /** + * @param int $jobId + * @param int $timestamp + * @return bool + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function kill(int $jobId, int $timestamp): bool + { + $schedule = $this->scheduleRepository->get($jobId); + if ($schedule->getStatus() !== Schedule::STATUS_RUNNING) { + return false; + } + $schedule->setData( + 'kill_request', + strftime(ScheduleManagementInterface::TIME_FORMAT, $this->dateTime->gmtTimestamp($timestamp)) + ); + $this->scheduleRepository->save($schedule); + return true; + } } diff --git a/Observer/ProcessKillRequestsObserver.php b/Observer/ProcessKillRequestsObserver.php new file mode 100644 index 00000000..17c4643e --- /dev/null +++ b/Observer/ProcessKillRequestsObserver.php @@ -0,0 +1,19 @@ +addPidToSchedule(); } + if (version_compare($context->getVersion(), '1.6.4') < 0) { + $this->addKillRequestToSchedule(); + } + $this->setup->endSetup(); } @@ -65,6 +69,30 @@ public function addPidToSchedule() "nullable" => true, "default" => null, "after" => "status", + ] + ); + } + + /** + * Add column to cron_schedule to send kill requests + */ + public function addKillRequestToSchedule() + { + if (version_compare($this->magentoMetaData->getVersion(), '2.3.0', '>=')) { + /* + * For Magento 2.3+, db_schema.xml is used instead + */ + return; + } + $this->setup->getConnection()->addColumn( + $this->setup->getTable("cron_schedule"), + "kill_request", + [ + "type" => Table::TYPE_TIMESTAMP, + "comment" => "Timestamp of kill request", + "nullable" => true, + "default" => null, + "after" => "pid", "unsigned" => true ] ); diff --git a/Test/Integration/Model/ScheduleManagementTest.php b/Test/Integration/Model/ScheduleManagementTest.php new file mode 100644 index 00000000..06cbea7b --- /dev/null +++ b/Test/Integration/Model/ScheduleManagementTest.php @@ -0,0 +1,84 @@ +objectManager = Bootstrap::getObjectManager(); + $this->scheduleManagement = $this->objectManager->get(ScheduleManagementInterface::class); + } + public function testKillRequestForRunningJobSucceeds() + { + $this->givenRunningSchedule($schedule); + $this->whenKillRequestedFor($schedule, strtotime(self::NOW)); + $this->thenScheduleHasKillRequest($schedule, self::NOW); + } + + public function testKillRequestForFinishedJobFails() + { + $this->givenFinishedSchedule($schedule); + $this->whenKillRequestedFor($schedule, strtotime(self::NOW)); + $this->thenScheduleDoesNotHaveKillRequest($schedule); + } + + private function givenRunningSchedule(&$schedule) + { + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_RUNNING); + $schedule->save(); + } + + private function givenFinishedSchedule(&$schedule) + { + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_SUCCESS); + $schedule->save(); + } + + private function whenKillRequestedFor(Schedule $schedule, $timestamp) + { + $this->scheduleManagement->kill((int)$schedule->getId(), $timestamp); + } + + private function thenScheduleHasKillRequest($schedule, $now) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($now, $schedule->getData('kill_request'), 'Kill request should be saved in schedule'); + } + + private function thenScheduleDoesNotHaveKillRequest($schedule) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertNull($schedule->getData('kill_request'), 'Kill request should not be saved in schedule'); + } +} diff --git a/Ui/Component/Listing/Column/CronActions.php b/Ui/Component/Listing/Column/CronActions.php index abd713a4..5bac9c97 100644 --- a/Ui/Component/Listing/Column/CronActions.php +++ b/Ui/Component/Listing/Column/CronActions.php @@ -13,9 +13,10 @@ class CronActions extends Column /** * Url path */ - const URL_PATH_EDIT = 'cronjobmanager/manage/edit'; - const URL_PATH_DELETE = 'cronjobmanager/manage_job/delete'; + const URL_PATH_EDIT = 'cronjobmanager/manage/edit'; + const URL_PATH_DELETE = 'cronjobmanager/manage_job/delete'; const URL_PATH_DISPATCH = 'cronjobmanager/manage_job/dispatch'; + const URL_PATH_KILL = 'cronjobmanager/manage_job/kill'; /** * @var UrlInterface @@ -104,6 +105,22 @@ public function prepareDataSource(array $dataSource) ), ], ], + 'kill' => [ + 'href' => $this->urlBuilder->getUrl( + static::URL_PATH_KILL, + [ + 'id' => $item['schedule_id'], + 'job_code' => $item['job_code'], + ] + ), + 'label' => __('Kill'), + 'confirm' => [ + 'job_code' => __('Kill'), + 'message' => __( + 'Are you sure you want to kill the process?' + ), + ], + ] ]; } } diff --git a/etc/crontab/events.xml b/etc/crontab/events.xml index b3ba1979..57fa6749 100644 --- a/etc/crontab/events.xml +++ b/etc/crontab/events.xml @@ -2,5 +2,6 @@ + diff --git a/etc/db_schema.xml b/etc/db_schema.xml index 4a5fea9f..509bc21f 100644 --- a/etc/db_schema.xml +++ b/etc/db_schema.xml @@ -3,5 +3,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> +
diff --git a/etc/module.xml b/etc/module.xml index dad70308..cec2ed82 100755 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/etc/webapi.xml b/etc/webapi.xml index 80fe6eb3..dbe1346c 100644 --- a/etc/webapi.xml +++ b/etc/webapi.xml @@ -46,6 +46,12 @@ + + + + + + From c2912efeca2c855be6c20ad016029872584e5024 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sun, 10 Feb 2019 10:57:51 +0100 Subject: [PATCH 06/10] Add integration test for CleanRunningJobsObserver --- Observer/ProcessKillRequestsObserver.php | 4 +- Test/Integration/CleanDeadJobsTest.php | 88 ++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 Test/Integration/CleanDeadJobsTest.php diff --git a/Observer/ProcessKillRequestsObserver.php b/Observer/ProcessKillRequestsObserver.php index 17c4643e..0aab81d3 100644 --- a/Observer/ProcessKillRequestsObserver.php +++ b/Observer/ProcessKillRequestsObserver.php @@ -10,10 +10,8 @@ class ProcessKillRequestsObserver implements ObserverInterface { public function execute(Observer $observer) { - // TODO: write integration test for CleanRunningJobsObserver // TODO: extract logic from CleanRunningJobsObserver to service // TODO: based on that, write service to process kill requests // TODO: call new service from ProcessKillRequestsObserver } - -} \ No newline at end of file +} diff --git a/Test/Integration/CleanDeadJobsTest.php b/Test/Integration/CleanDeadJobsTest.php new file mode 100644 index 00000000..b78bcf11 --- /dev/null +++ b/Test/Integration/CleanDeadJobsTest.php @@ -0,0 +1,88 @@ +objectManager = Bootstrap::getObjectManager(); + $this->eventManager = $this->objectManager->get(Event\ManagerInterface::class); + } + + public function testDeadRunningJobsAreCleaned() + { + $this->givenRunningScheduleWithInactiveProcess($schedule); + $this->whenEventIsDispatched('process_cron_queue_before'); + $this->thenScheduleHasStatus($schedule, Schedule::STATUS_ERROR); + $this->andScheduleHasMessage($schedule, 'Process went away'); + } + + public function testActiveRunningJobsAreNotCleaned() + { + $this->givenRunningScheduleWithActiveProcess($schedule); + $this->whenEventIsDispatched('process_cron_queue_before'); + $this->thenScheduleHasStatus($schedule, Schedule::STATUS_RUNNING); + } + + private function givenRunningScheduleWithInactiveProcess(&$schedule) + { + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_RUNNING); + $schedule->setData('pid', self::DEAD_PID); + $schedule->save(); + } + + private function givenRunningScheduleWithActiveProcess(&$schedule) + { + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_RUNNING); + $schedule->setData('pid', \getmypid()); + $schedule->save(); + } + + private function whenEventIsDispatched($eventName) + { + $this->eventManager->dispatch($eventName); + } + + private function thenScheduleHasStatus(Schedule $schedule, $expectedStatus) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($expectedStatus, $schedule->getStatus(), 'Schedule should have expected status'); + } + + private function andScheduleHasMessage(Schedule $schedule, $expectedMessage) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($expectedMessage, $schedule->getMessages(), 'Schedule should have expected message'); + } +} From d451ad19d714c0bb5d9fe5641579f9030ea3cbf8 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Sun, 10 Feb 2019 11:47:38 +0100 Subject: [PATCH 07/10] Refactor CleanRunningJobObserver - delegate execution to service - use repository to load and save schedules - extract ProcessManagement class for system processes --- Api/Data/ScheduleInterface.php | 12 ++++ Api/ScheduleManagementInterface.php | 1 + Api/ScheduleRepositoryAdapterInterface.php | 9 +++ Model/CleanRunningJobs.php | 72 +++++++++++++++++++ Model/ProcessManagement.php | 16 +++++ Model/ScheduleManagement.php | 1 + Model/ScheduleRepositoryAdapter.php | 17 ++++- Observer/CleanRunningJobsObserver.php | 58 +++------------ Observer/ProcessKillRequestsObserver.php | 1 - ...dJobsTest.php => CleanRunningJobsTest.php} | 2 +- 10 files changed, 136 insertions(+), 53 deletions(-) create mode 100644 Model/CleanRunningJobs.php create mode 100644 Model/ProcessManagement.php rename Test/Integration/{CleanDeadJobsTest.php => CleanRunningJobsTest.php} (98%) diff --git a/Api/Data/ScheduleInterface.php b/Api/Data/ScheduleInterface.php index 8157b808..9b837d7b 100644 --- a/Api/Data/ScheduleInterface.php +++ b/Api/Data/ScheduleInterface.php @@ -2,8 +2,20 @@ namespace EthanYehuda\CronjobManager\Api\Data; +use Magento\Cron\Model\Schedule; + interface ScheduleInterface { + public const STATUS_PENDING = Schedule::STATUS_PENDING; + + public const STATUS_RUNNING = Schedule::STATUS_RUNNING; + + public const STATUS_SUCCESS = Schedule::STATUS_SUCCESS; + + public const STATUS_MISSED = Schedule::STATUS_MISSED; + + public const STATUS_ERROR = Schedule::STATUS_ERROR; + /** * @return int */ diff --git a/Api/ScheduleManagementInterface.php b/Api/ScheduleManagementInterface.php index fb6ee6c3..64cd24f9 100644 --- a/Api/ScheduleManagementInterface.php +++ b/Api/ScheduleManagementInterface.php @@ -60,4 +60,5 @@ public function flush(): bool; * @return bool */ public function kill(int $jobId, int $timestamp): bool; + } diff --git a/Api/ScheduleRepositoryAdapterInterface.php b/Api/ScheduleRepositoryAdapterInterface.php index b5ae640a..7f2cbea5 100644 --- a/Api/ScheduleRepositoryAdapterInterface.php +++ b/Api/ScheduleRepositoryAdapterInterface.php @@ -2,6 +2,7 @@ namespace EthanYehuda\CronjobManager\Api; +use EthanYehuda\CronjobManager\Api\Data\ScheduleInterface; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; @@ -31,4 +32,12 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr * @throws CouldNotSaveException */ public function save(\EthanYehuda\CronjobManager\Api\Data\ScheduleInterface $schedule, $scheduleId = null): \EthanYehuda\CronjobManager\Api\Data\ScheduleInterface; + + /** + * Return all jobs with given status + * + * @param string $status + * @return ScheduleInterface[] + */ + public function getByStatus($status); } diff --git a/Model/CleanRunningJobs.php b/Model/CleanRunningJobs.php new file mode 100644 index 00000000..6799a3c6 --- /dev/null +++ b/Model/CleanRunningJobs.php @@ -0,0 +1,72 @@ +collectionFactory = $collectionFactory; + $this->resourceModel = $resourceModel; + $this->processManagement = $processManagement; + $this->scheduleRepository = $scheduleRepository; + } + + /** + * Find all jobs in status "running" (according to db), + * and check if the process is alive. If not, set status to error, with the message + * "Process went away" + */ + public function execute() + { + $runningJobs = $this->scheduleRepository->getByStatus(ScheduleInterface::STATUS_RUNNING); + + foreach ($runningJobs as $schedule) { + if ($this->processManagement->isPidAlive($schedule->getPid())) { + continue; + } + + $messages = []; + if ($schedule->getMessages()) { + $messages[] = $schedule->getMessages(); + } + + $messages[] = 'Process went away'; + + $schedule + ->setStatus(Schedule::STATUS_ERROR) + ->setMessages(implode("\n", $messages)); + + $this->scheduleRepository->save($schedule); + } + } +} diff --git a/Model/ProcessManagement.php b/Model/ProcessManagement.php new file mode 100644 index 00000000..5881f489 --- /dev/null +++ b/Model/ProcessManagement.php @@ -0,0 +1,16 @@ +scheduleRepository->save($schedule); return true; } + } diff --git a/Model/ScheduleRepositoryAdapter.php b/Model/ScheduleRepositoryAdapter.php index bfe38cca..431d2850 100644 --- a/Model/ScheduleRepositoryAdapter.php +++ b/Model/ScheduleRepositoryAdapter.php @@ -2,10 +2,12 @@ namespace EthanYehuda\CronjobManager\Model; +use EthanYehuda\CronjobManager\Api\Data\ScheduleInterface; use EthanYehuda\CronjobManager\Api\ScheduleRepositoryAdapterInterface; use EthanYehuda\CronjobManager\Api\ScheduleRepositoryInterface; use EthanYehuda\CronjobManager\Api\Data\ScheduleInterfaceFactory; use Magento\Cron\Model\ScheduleFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; class ScheduleRepositoryAdapter implements ScheduleRepositoryAdapterInterface { @@ -23,15 +25,21 @@ class ScheduleRepositoryAdapter implements ScheduleRepositoryAdapterInterface * @var ScheduleFactory */ private $coreScheduleFactory; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; public function __construct( ScheduleRepositoryInterface $scheduleRepository, ScheduleInterfaceFactory $scheduleFactory, - ScheduleFactory $coreScheduleFactory + ScheduleFactory $coreScheduleFactory, + SearchCriteriaBuilder $searchCriteriaBuilder ) { $this->scheduleRepository = $scheduleRepository; $this->scheduleFactory = $scheduleFactory; $this->coreScheduleFactory = $coreScheduleFactory; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } public function get(int $scheduleId): \EthanYehuda\CronjobManager\Api\Data\ScheduleInterface @@ -66,4 +74,11 @@ public function save(\EthanYehuda\CronjobManager\Api\Data\ScheduleInterface $sch return $this->scheduleFactory->create(['data' => $coreSchedule->getData()]); } + + public function getByStatus($status) + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('status', $status)->create(); + return $this->getList($searchCriteria)->getItems(); + } + } diff --git a/Observer/CleanRunningJobsObserver.php b/Observer/CleanRunningJobsObserver.php index d1a3809f..846846c0 100644 --- a/Observer/CleanRunningJobsObserver.php +++ b/Observer/CleanRunningJobsObserver.php @@ -3,8 +3,7 @@ namespace EthanYehuda\CronjobManager\Observer; use EthanYehuda\CronjobManager\Helper\Config; -use Magento\Cron\Model\Schedule; -use Magento\Cron\Model\ResourceModel\Schedule as ScheduleResource; +use EthanYehuda\CronjobManager\Model\CleanRunningJobs; use Magento\Cron\Model\ResourceModel\Schedule\CollectionFactory; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\Observer; @@ -12,23 +11,18 @@ class CleanRunningJobsObserver implements ObserverInterface { - /** @var \Magento\Cron\Model\ResourceModel\Schedule\CollectionFactory */ - private $collectionFactory; - - /** @var \Magento\Cron\Model\ResourceModel\Schedule */ - private $resourceModel; - /** @var \Magento\Framework\App\Config\ScopeConfigInterface */ private $scopeConfig; + /** @var CleanRunningJobs */ + private $cleanRunningJobs; + public function __construct( - CollectionFactory $collectionFactory, - ScheduleResource $resourceModel, + CleanRunningJobs $cleanRunningJobs, ScopeConfigInterface $scopeConfig ) { - $this->collectionFactory = $collectionFactory; - $this->resourceModel = $resourceModel; $this->scopeConfig = $scopeConfig; + $this->cleanRunningJobs = $cleanRunningJobs; } /** @@ -45,42 +39,6 @@ public function execute(Observer $observer) return; } - /** @var \Magento\Cron\Model\ResourceModel\Schedule\Collection $collection */ - $collection = $this->collectionFactory->create(); - - $collection->addFieldToFilter("status", Schedule::STATUS_RUNNING); - - /** @var Schedule $schedule */ - foreach ($collection->getItems() as $schedule) { - if ($this->isPidAlive($schedule->getData("pid"))) { - continue; - } - - $messages = []; - if ($schedule->getMessages()) { - $messages[] = $schedule->getMessages(); - } - - $messages[] = "Process went away"; - - $schedule - ->setStatus(Schedule::STATUS_ERROR) - ->setMessages(join("\n", $messages)); - - $this->resourceModel->save($schedule); - } - } - - /** - * @param int $pid - * @return boolean - */ - private function isPidAlive($pid) - { - if (file_exists("/proc/" . intval($pid))) { - return true; - } - - return false; + $this->cleanRunningJobs->execute(); } -} \ No newline at end of file +} diff --git a/Observer/ProcessKillRequestsObserver.php b/Observer/ProcessKillRequestsObserver.php index 0aab81d3..b4c20139 100644 --- a/Observer/ProcessKillRequestsObserver.php +++ b/Observer/ProcessKillRequestsObserver.php @@ -10,7 +10,6 @@ class ProcessKillRequestsObserver implements ObserverInterface { public function execute(Observer $observer) { - // TODO: extract logic from CleanRunningJobsObserver to service // TODO: based on that, write service to process kill requests // TODO: call new service from ProcessKillRequestsObserver } diff --git a/Test/Integration/CleanDeadJobsTest.php b/Test/Integration/CleanRunningJobsTest.php similarity index 98% rename from Test/Integration/CleanDeadJobsTest.php rename to Test/Integration/CleanRunningJobsTest.php index b78bcf11..8802795a 100644 --- a/Test/Integration/CleanDeadJobsTest.php +++ b/Test/Integration/CleanRunningJobsTest.php @@ -12,7 +12,7 @@ /** * @magentoAppArea crontab */ -class CleanDeadJobsTest extends TestCase +class CleanRunningJobsTest extends TestCase { /** * @var ObjectManager From 1cddf1ece50af4dc59ddf0f920ea0f2251c47b84 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Mon, 11 Feb 2019 08:33:02 +0100 Subject: [PATCH 08/10] Add kill request --- Api/Data/ScheduleInterface.php | 13 ++ Model/CleanRunningJobs.php | 28 ++-- Model/Clock.php | 9 ++ Model/Data/Schedule.php | 29 ++-- Model/ProcessKillRequests.php | 67 +++++++++ Model/ProcessManagement.php | 21 ++- Model/Schedule/Source/Status.php | 15 +- Model/SystemClock.php | 12 ++ Observer/ProcessKillRequestsObserver.php | 14 +- Test/Integration/CleanRunningJobsTest.php | 14 +- Test/Integration/ProcessKillRequestsTest.php | 140 +++++++++++++++++++ Test/Util/FakeClock.php | 30 ++++ etc/di.xml | 1 + 13 files changed, 360 insertions(+), 33 deletions(-) create mode 100644 Model/Clock.php create mode 100644 Model/ProcessKillRequests.php create mode 100644 Model/SystemClock.php create mode 100644 Test/Integration/ProcessKillRequestsTest.php create mode 100644 Test/Util/FakeClock.php diff --git a/Api/Data/ScheduleInterface.php b/Api/Data/ScheduleInterface.php index 9b837d7b..63634ce1 100644 --- a/Api/Data/ScheduleInterface.php +++ b/Api/Data/ScheduleInterface.php @@ -16,6 +16,8 @@ interface ScheduleInterface public const STATUS_ERROR = Schedule::STATUS_ERROR; + public const STATUS_KILLED = 'killed'; + /** * @return int */ @@ -61,6 +63,11 @@ public function getExecutedAt(); */ public function getFinishedAt(); + /** + * @return string|null + */ + public function getKillRequest(); + /** * @param int $scheduleId * @return ScheduleInterface @@ -114,4 +121,10 @@ public function setExecutedAt(string $executedAt): self; * @return ScheduleInterface */ public function setFinishedAt(string $finishedAt): self; + + /** + * @param string $killRequest + * @return ScheduleInterface + */ + public function setKillRequest(string $killRequest): self; } diff --git a/Model/CleanRunningJobs.php b/Model/CleanRunningJobs.php index 6799a3c6..f71079bb 100644 --- a/Model/CleanRunningJobs.php +++ b/Model/CleanRunningJobs.php @@ -5,21 +5,15 @@ use EthanYehuda\CronjobManager\Api\Data\ScheduleInterface; use EthanYehuda\CronjobManager\Api\ScheduleRepositoryAdapterInterface; -use Magento\Cron\Model\ResourceModel\Schedule as ScheduleResource; use Magento\Cron\Model\ResourceModel\Schedule\CollectionFactory; use Magento\Cron\Model\Schedule; +use Magento\Framework\Stdlib\DateTime\DateTime; /** * Update jobs with dead processes from running to error */ class CleanRunningJobs { - /** @var \Magento\Cron\Model\ResourceModel\Schedule\CollectionFactory */ - private $collectionFactory; - - /** @var \Magento\Cron\Model\ResourceModel\Schedule */ - private $resourceModel; - /** * @var ProcessManagement */ @@ -28,17 +22,25 @@ class CleanRunningJobs * @var ScheduleRepositoryAdapterInterface */ private $scheduleRepository; + /** + * @var Clock + */ + private $clock; + /** + * @var DateTime + */ + private $dateTime; public function __construct( - CollectionFactory $collectionFactory, - ScheduleResource $resourceModel, ScheduleRepositoryAdapterInterface $scheduleRepository, - ProcessManagement $processManagement + ProcessManagement $processManagement, + DateTime $dateTime, + Clock $clock ) { - $this->collectionFactory = $collectionFactory; - $this->resourceModel = $resourceModel; $this->processManagement = $processManagement; $this->scheduleRepository = $scheduleRepository; + $this->dateTime = $dateTime; + $this->clock = $clock; } /** @@ -60,7 +62,7 @@ public function execute() $messages[] = $schedule->getMessages(); } - $messages[] = 'Process went away'; + $messages[] = 'Process went away at ' . $this->dateTime->gmtDate(null, $this->clock->now()); $schedule ->setStatus(Schedule::STATUS_ERROR) diff --git a/Model/Clock.php b/Model/Clock.php new file mode 100644 index 00000000..cb62e938 --- /dev/null +++ b/Model/Clock.php @@ -0,0 +1,9 @@ +getData(self::KEY_FINISHED_AT); } + public function getKillRequest() + { + return $this->getData(self::KEY_KILL_REQUEST); + } + public function setScheduleId(int $scheduleId): \EthanYehuda\CronjobManager\Api\Data\ScheduleInterface { $this->setData(self::KEY_SCHEDULE_ID, $scheduleId); @@ -111,6 +118,12 @@ public function setScheduledAt(string $scheduledAt): \EthanYehuda\CronjobManager return $this; } + public function setKillRequest(string $killRequest): \EthanYehuda\CronjobManager\Api\Data\ScheduleInterface + { + $this->setData(self::KEY_KILL_REQUEST, $killRequest); + return $this; + } + public function setExecutedAt(string $executedAt): \EthanYehuda\CronjobManager\Api\Data\ScheduleInterface { $this->setData(self::KEY_EXECUTED_AT, $executedAt); diff --git a/Model/ProcessKillRequests.php b/Model/ProcessKillRequests.php new file mode 100644 index 00000000..48d88dee --- /dev/null +++ b/Model/ProcessKillRequests.php @@ -0,0 +1,67 @@ +processManagement = $processManagement; + $this->scheduleRepository = $scheduleRepository; + $this->dateTime = $dateTime; + $this->clock = $clock; + } + + public function execute() + { + $runningJobs = $this->scheduleRepository->getByStatus(ScheduleInterface::STATUS_RUNNING); + foreach ($runningJobs as $schedule) { + if ($schedule->getKillRequest() && $schedule->getKillRequest() <= \time() && $schedule->getPid()) { + $this->killScheduleProcess($schedule); + } + } + } + + private function killScheduleProcess(ScheduleInterface $schedule): void + { + if ($this->processManagement->killPid($schedule->getPid())) { + $messages = []; + if ($schedule->getMessages()) { + $messages[] = $schedule->getMessages(); + } + $messages[] = 'Process was killed at ' . $this->dateTime->gmtDate(null, $this->clock->now()); + $schedule + ->setMessages(\implode("\n", $messages)) + ->setStatus(ScheduleInterface::STATUS_KILLED); + + $this->scheduleRepository->save($schedule); + } + } +} diff --git a/Model/ProcessManagement.php b/Model/ProcessManagement.php index 5881f489..8f485e8f 100644 --- a/Model/ProcessManagement.php +++ b/Model/ProcessManagement.php @@ -5,12 +5,27 @@ class ProcessManagement { + private const SIGKILL = 9; + public function isPidAlive(int $pid): bool { - if (file_exists("/proc/" . intval($pid))) { - return true; + return \file_exists('/proc/' . $pid); + } + + public function killPid($pid): bool + { + if (!$this->isPidAlive($pid)) { + return false; + } + //TODO first try to send SIGINT, wait up to X seconds, then send SIGKILL if process still running + $killed = \posix_kill($pid, self::SIGKILL); + if ($killed && !$this->isPidAlive($pid)) { + \sleep(5); + if ($this->isPidAlive($pid)) { + return false; + } } + return $killed; - return false; } } diff --git a/Model/Schedule/Source/Status.php b/Model/Schedule/Source/Status.php index 2b5373bd..317dca02 100755 --- a/Model/Schedule/Source/Status.php +++ b/Model/Schedule/Source/Status.php @@ -2,6 +2,7 @@ namespace EthanYehuda\CronjobManager\Model\Schedule\Source; +use EthanYehuda\CronjobManager\Api\Data\ScheduleInterface; use Magento\Cron\Model\Schedule; use Magento\Framework\Data\OptionSourceInterface; @@ -16,25 +17,29 @@ public function toOptionArray() { return [ [ - 'value' => Schedule::STATUS_ERROR, + 'value' => ScheduleInterface::STATUS_ERROR, 'label' => __('Error'), ], [ - 'value' => Schedule::STATUS_MISSED, + 'value' => ScheduleInterface::STATUS_MISSED, 'label' => __('Missed'), ], [ - 'value' => Schedule::STATUS_PENDING, + 'value' => ScheduleInterface::STATUS_PENDING, 'label' => __('Pending'), ], [ - 'value' => Schedule::STATUS_RUNNING, + 'value' => ScheduleInterface::STATUS_RUNNING, 'label' => __('Running'), ], [ - 'value' => Schedule::STATUS_SUCCESS, + 'value' => ScheduleInterface::STATUS_SUCCESS, 'label' => __('Success'), ], + [ + 'value' => ScheduleInterface::STATUS_KILLED, + 'label' => __('Killed'), + ], ]; } } diff --git a/Model/SystemClock.php b/Model/SystemClock.php new file mode 100644 index 00000000..aa29de6b --- /dev/null +++ b/Model/SystemClock.php @@ -0,0 +1,12 @@ +processKillRequests = $processKillRequests; + } + public function execute(Observer $observer) { - // TODO: based on that, write service to process kill requests - // TODO: call new service from ProcessKillRequestsObserver + $this->processKillRequests->execute(); } } diff --git a/Test/Integration/CleanRunningJobsTest.php b/Test/Integration/CleanRunningJobsTest.php index 8802795a..a7a7b82e 100644 --- a/Test/Integration/CleanRunningJobsTest.php +++ b/Test/Integration/CleanRunningJobsTest.php @@ -3,6 +3,8 @@ namespace EthanYehuda\CronjobManager\Test\Integration; +use EthanYehuda\CronjobManager\Model\Clock; +use EthanYehuda\CronjobManager\Test\Util\FakeClock; use Magento\Cron\Model\Schedule; use Magento\Framework\ObjectManager\ObjectManager; use Magento\TestFramework\Helper\Bootstrap; @@ -10,25 +12,33 @@ use Magento\Framework\Event; /** + * @magentoAppIsolation enabled * @magentoAppArea crontab */ class CleanRunningJobsTest extends TestCase { + private const NOW = '2019-02-09 18:33:00'; /** * @var ObjectManager */ private $objectManager; - /** * @var Event\ManagerInterface */ private $eventManager; + /** + * @var FakeClock + */ + private $clock; private const DEAD_PID = 99999999; protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); + $this->objectManager->configure(['preferences' => [Clock::class => FakeClock::class]]); + $this->clock = $this->objectManager->get(Clock::class); + $this->clock->setTimestamp(strtotime(self::NOW)); $this->eventManager = $this->objectManager->get(Event\ManagerInterface::class); } @@ -37,7 +47,7 @@ public function testDeadRunningJobsAreCleaned() $this->givenRunningScheduleWithInactiveProcess($schedule); $this->whenEventIsDispatched('process_cron_queue_before'); $this->thenScheduleHasStatus($schedule, Schedule::STATUS_ERROR); - $this->andScheduleHasMessage($schedule, 'Process went away'); + $this->andScheduleHasMessage($schedule, 'Process went away at ' . self::NOW); } public function testActiveRunningJobsAreNotCleaned() diff --git a/Test/Integration/ProcessKillRequestsTest.php b/Test/Integration/ProcessKillRequestsTest.php new file mode 100644 index 00000000..2d631b43 --- /dev/null +++ b/Test/Integration/ProcessKillRequestsTest.php @@ -0,0 +1,140 @@ +objectManager = Bootstrap::getObjectManager(); + $this->objectManager->configure(['preferences' => [Clock::class => FakeClock::class]]); + $this->clock = $this->objectManager->get(Clock::class); + $this->clock->setTimestamp(strtotime(self::NOW)); + $this->eventManager = $this->objectManager->get(Event\ManagerInterface::class); + $this->scheduleManagement = $this->objectManager->get(ScheduleManagementInterface::class); + $this->processManagement = $this->objectManager->get(ProcessManagement::class); + } + + protected function tearDown() + { + /* + * Take care of children that we failed to kill + */ + if ($this->childPid) { + \posix_kill($this->childPid, SIGKILL); + } + } + + public function testDeadRunningJobsAreCleaned() + { + $this->givenRunningScheduleWithKillRequest($schedule, $this->timeStampInThePast()); + $this->whenEventIsDispatched('process_cron_queue_before'); + $this->thenScheduleHasStatus($schedule, ScheduleInterface::STATUS_KILLED); + $this->andScheduleHasMessage($schedule, 'Process was killed at ' . self::NOW); + $this->andProcessIsKilled($schedule); + } + + private function givenRunningScheduleWithKillRequest(&$schedule, int $timestamp) + { + + /** @var Schedule $schedule */ + $schedule = $this->objectManager->create(Schedule::class); + $schedule->setStatus(Schedule::STATUS_RUNNING); + $schedule->setData('pid', $this->createProcessToKill()); + $schedule->save(); + $this->scheduleManagement->kill((int)$schedule->getId(), $timestamp); + } + + private function whenEventIsDispatched($eventName) + { + $this->eventManager->dispatch($eventName); + } + + private function thenScheduleHasStatus(Schedule $schedule, $expectedStatus) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($expectedStatus, $schedule->getStatus(), 'Schedule should have expected status'); + } + + private function andScheduleHasMessage(Schedule $schedule, $expectedMessage) + { + /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */ + $scheduleResource = $this->objectManager->get(\Magento\Cron\Model\ResourceModel\Schedule::class); + $scheduleResource->load($schedule, $schedule->getId()); + $this->assertEquals($expectedMessage, $schedule->getMessages(), 'Schedule should have expected message'); + } + + private function timeStampInThePast(): int + { + return $this->clock->now() - 1; + } + + private function createProcessToKill(): int + { + $pid = \pcntl_fork(); + if ($pid === -1) { + $this->fail('Could not fork process to test killing'); + } elseif ($pid) { + $this->assertTrue($this->processManagement->isPidAlive($pid), 'Precondition: child is alive'); + $this->childPid = $pid; + return $pid; + } else { + // we are the child, waiting to be killed + while (true) { + sleep(1); + } + } + return 0; + } + + private function andProcessIsKilled(Schedule $schedule) + { + \pcntl_wait($status); // killed children are zombies until we wait for them + $pid = (int)$schedule->getData('pid'); + $this->assertFalse($this->processManagement->isPidAlive($pid), "Child with PID {$pid} should be killed"); + } +} diff --git a/Test/Util/FakeClock.php b/Test/Util/FakeClock.php new file mode 100644 index 00000000..f5742670 --- /dev/null +++ b/Test/Util/FakeClock.php @@ -0,0 +1,30 @@ +timestamp = $timestamp; + } + + public function setTimestamp(int $timestamp): void + { + $this->timestamp = $timestamp; + } + + public function now(): int + { + return $this->timestamp; + } + +} diff --git a/etc/di.xml b/etc/di.xml index c7be1959..8caf3363 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -6,6 +6,7 @@ + cron_schedule From 95894c772a9363d71cf6e6bd9c163664369cd8c2 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Fri, 22 Feb 2019 10:26:17 +0100 Subject: [PATCH 09/10] Retain PHP 7.0 compatibility for now --- Api/Data/ScheduleInterface.php | 12 ++++++------ Model/ProcessManagement.php | 2 +- Test/Integration/CleanRunningJobsTest.php | 4 ++-- Test/Integration/Model/ScheduleManagementTest.php | 2 +- Test/Integration/ProcessKillRequestsTest.php | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Api/Data/ScheduleInterface.php b/Api/Data/ScheduleInterface.php index 63634ce1..e6308482 100644 --- a/Api/Data/ScheduleInterface.php +++ b/Api/Data/ScheduleInterface.php @@ -6,17 +6,17 @@ interface ScheduleInterface { - public const STATUS_PENDING = Schedule::STATUS_PENDING; + const STATUS_PENDING = Schedule::STATUS_PENDING; - public const STATUS_RUNNING = Schedule::STATUS_RUNNING; + const STATUS_RUNNING = Schedule::STATUS_RUNNING; - public const STATUS_SUCCESS = Schedule::STATUS_SUCCESS; + const STATUS_SUCCESS = Schedule::STATUS_SUCCESS; - public const STATUS_MISSED = Schedule::STATUS_MISSED; + const STATUS_MISSED = Schedule::STATUS_MISSED; - public const STATUS_ERROR = Schedule::STATUS_ERROR; + const STATUS_ERROR = Schedule::STATUS_ERROR; - public const STATUS_KILLED = 'killed'; + const STATUS_KILLED = 'killed'; /** * @return int diff --git a/Model/ProcessManagement.php b/Model/ProcessManagement.php index 8f485e8f..27d36ba3 100644 --- a/Model/ProcessManagement.php +++ b/Model/ProcessManagement.php @@ -5,7 +5,7 @@ class ProcessManagement { - private const SIGKILL = 9; + const SIGKILL = 9; public function isPidAlive(int $pid): bool { diff --git a/Test/Integration/CleanRunningJobsTest.php b/Test/Integration/CleanRunningJobsTest.php index a7a7b82e..6e3ef583 100644 --- a/Test/Integration/CleanRunningJobsTest.php +++ b/Test/Integration/CleanRunningJobsTest.php @@ -17,7 +17,7 @@ */ class CleanRunningJobsTest extends TestCase { - private const NOW = '2019-02-09 18:33:00'; + const NOW = '2019-02-09 18:33:00'; /** * @var ObjectManager */ @@ -31,7 +31,7 @@ class CleanRunningJobsTest extends TestCase */ private $clock; - private const DEAD_PID = 99999999; + const DEAD_PID = 99999999; protected function setUp() { diff --git a/Test/Integration/Model/ScheduleManagementTest.php b/Test/Integration/Model/ScheduleManagementTest.php index 06cbea7b..7582c3e5 100644 --- a/Test/Integration/Model/ScheduleManagementTest.php +++ b/Test/Integration/Model/ScheduleManagementTest.php @@ -24,7 +24,7 @@ class ScheduleManagementTest extends TestCase */ private $objectManager; - private const NOW = '2019-02-09 18:33:00'; + const NOW = '2019-02-09 18:33:00'; protected function setUp() { diff --git a/Test/Integration/ProcessKillRequestsTest.php b/Test/Integration/ProcessKillRequestsTest.php index 2d631b43..873b5b75 100644 --- a/Test/Integration/ProcessKillRequestsTest.php +++ b/Test/Integration/ProcessKillRequestsTest.php @@ -20,7 +20,7 @@ */ class ProcessKillRequestsTest extends TestCase { - private const NOW = '2019-02-09 18:33:00'; + const NOW = '2019-02-09 18:33:00'; /** * @var int */ From 2225703711d5259c1ea456f93eac7033fd10604c Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Fri, 22 Feb 2019 10:52:29 +0100 Subject: [PATCH 10/10] Translate message --- Model/CleanRunningJobs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/CleanRunningJobs.php b/Model/CleanRunningJobs.php index f71079bb..46f8f624 100644 --- a/Model/CleanRunningJobs.php +++ b/Model/CleanRunningJobs.php @@ -62,7 +62,7 @@ public function execute() $messages[] = $schedule->getMessages(); } - $messages[] = 'Process went away at ' . $this->dateTime->gmtDate(null, $this->clock->now()); + $messages[] = __('Process went away at %1', $this->dateTime->gmtDate(null, $this->clock->now())); $schedule ->setStatus(Schedule::STATUS_ERROR)