From 4a68320ad12056ad6ba2d6f7be713464baf0c46f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 11:43:03 +0200 Subject: [PATCH] feat(TaskProcessing): Add setup check for task processing worker status Signed-off-by: Marcel Klehr --- .../composer/composer/autoload_classmap.php | 1 + .../composer/composer/autoload_static.php | 1 + .../TaskProcessingWorkerIsRunning.php | 64 +++++++++++++++ .../TaskProcessingWorkerIsRunningTest.php | 78 +++++++++++++++++++ core/Command/TaskProcessing/WorkerCommand.php | 3 + 5 files changed, 147 insertions(+) create mode 100644 apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php create mode 100644 apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index f856bd7aa8c..e8557c195a8 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -136,6 +136,7 @@ return array( 'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php', 'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => $baseDir . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php', 'OCA\\Settings\\SetupChecks\\TaskProcessingSuccessRate' => $baseDir . '/../lib/SetupChecks/TaskProcessingSuccessRate.php', + 'OCA\\Settings\\SetupChecks\\TaskProcessingWorkerIsRunning' => $baseDir . '/../lib/SetupChecks/TaskProcessingWorkerIsRunning.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => $baseDir . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => $baseDir . '/../lib/SetupChecks/TransactionIsolation.php', 'OCA\\Settings\\SetupChecks\\TwoFactorConfiguration' => $baseDir . '/../lib/SetupChecks/TwoFactorConfiguration.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index d1c8d9b9eae..027328f3f36 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -151,6 +151,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php', 'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php', 'OCA\\Settings\\SetupChecks\\TaskProcessingSuccessRate' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingSuccessRate.php', + 'OCA\\Settings\\SetupChecks\\TaskProcessingWorkerIsRunning' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingWorkerIsRunning.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => __DIR__ . '/..' . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => __DIR__ . '/..' . '/../lib/SetupChecks/TransactionIsolation.php', 'OCA\\Settings\\SetupChecks\\TwoFactorConfiguration' => __DIR__ . '/..' . '/../lib/SetupChecks/TwoFactorConfiguration.php', diff --git a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php new file mode 100644 index 00000000000..0ccc1d3e7ac --- /dev/null +++ b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php @@ -0,0 +1,64 @@ +l10n->t('Task Processing worker status'); + } + + public function run(): SetupResult { + $lastNDays = self::HAS_TASKS_IN_LAST_X_DAYS; + $tasks = $this->taskProcessingManager->getTasks(userId: '', scheduleAfter: $this->timeFactory->now()->getTimestamp() - (60 * 60 * 24 * $lastNDays)); + $taskCount = count($tasks); + if ($taskCount === 0) { + // In case taskprocessing is not used at all + return SetupResult::success( + $this->l10n->n( + 'No scheduled tasks in the last day.', + 'No scheduled tasks in the last %n days.', + $lastNDays + ) + ); + } + $lastIteration = (int)$this->appConfig->getValueString('core', 'taskprocessing_worker_last_iteration', lazy: true); + if ($lastIteration > $this->timeFactory->now()->getTimestamp() - 60 * self::IS_RUNNING_IN_LAST_X_MINUTES) { + return SetupResult::success( + $this->l10n->n('The Task Processing worker has run in the last minute.', 'The Task Processing worker has run in the last %n minute.', self::IS_RUNNING_IN_LAST_X_MINUTES) + ); + } + return SetupResult::warning( + $this->l10n->t('The Task Processing worker does not seem to be running. The last run was at %s.', [date('Y-m-d H:i:s', (int)$this->appConfig->getValueString('core', 'taskprocessing_worker_last_iteration'))]) + ); + } +} diff --git a/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php b/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php new file mode 100644 index 00000000000..61b9dd95932 --- /dev/null +++ b/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php @@ -0,0 +1,78 @@ +l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->timeFactory = $this->getMockBuilder(ITimeFactory::class)->getMock(); + $this->taskProcessingManager = $this->getMockBuilder(IManager::class)->getMock(); + $this->appConfig = $this->getMockBuilder(IAppConfig::class)->getMock(); + + $this->check = new TaskProcessingWorkerIsRunning( + $this->l10n, + $this->taskProcessingManager, + $this->timeFactory, + $this->appConfig + ); + } + + public function testPass(): void { + $tasks = []; + for ($i = 0; $i < 10; $i++) { + $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); + $task->setStartedAt($this->timeFactory->now()->getTimestamp()); + $task->setScheduledAt($this->timeFactory->now()->getTimestamp()); + $task->setEndedAt($this->timeFactory->now()->getTimestamp()); + $task->setStatus(Task::STATUS_SUCCESSFUL); + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->appConfig->method('getValueString')->willReturn((string)$this->timeFactory->now()->getTimestamp()); + + $this->assertEquals(SetupResult::SUCCESS, $this->check->run()->getSeverity()); + } + + public function testFail(): void { + $tasks = []; + for ($i = 0; $i < 10; $i++) { + $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); + $task->setStartedAt($this->timeFactory->now()->getTimestamp()); + $task->setScheduledAt($this->timeFactory->now()->getTimestamp()); + $task->setEndedAt($this->timeFactory->now()->getTimestamp()); + $task->setStatus(Task::STATUS_SUCCESSFUL); + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->appConfig->method('getValueString')->willReturn((string)($this->timeFactory->now()->getTimestamp() - 60 * 10)); + + $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); + } +} diff --git a/core/Command/TaskProcessing/WorkerCommand.php b/core/Command/TaskProcessing/WorkerCommand.php index 09f3f3573d2..15719f5245f 100644 --- a/core/Command/TaskProcessing/WorkerCommand.php +++ b/core/Command/TaskProcessing/WorkerCommand.php @@ -10,6 +10,7 @@ namespace OC\Core\Command\TaskProcessing; use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; +use OCP\IAppConfig; use OCP\TaskProcessing\Exception\Exception; use OCP\TaskProcessing\Exception\NotFoundException; use OCP\TaskProcessing\IManager; @@ -23,6 +24,7 @@ class WorkerCommand extends Base { public function __construct( private readonly IManager $taskProcessingManager, private readonly LoggerInterface $logger, + private readonly IAppConfig $appConfig, ) { parent::__construct(); } @@ -87,6 +89,7 @@ class WorkerCommand extends Base { break; } + $this->appConfig->setValueString('core', 'taskprocessing_worker_last_iteration', (string)time(), lazy: true); $processedTask = $this->processNextTask($output, $taskTypes); if ($once) {