From 4a68320ad12056ad6ba2d6f7be713464baf0c46f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 11:43:03 +0200 Subject: [PATCH 01/10] 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 f856bd7aa8c42..e8557c195a87b 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -136,6 +136,7 @@ '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 d1c8d9b9eae77..027328f3f3672 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 0000000000000..0ccc1d3e7ac83 --- /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 0000000000000..61b9dd95932a2 --- /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 09f3f3573d23c..15719f5245fa1 100644 --- a/core/Command/TaskProcessing/WorkerCommand.php +++ b/core/Command/TaskProcessing/WorkerCommand.php @@ -10,6 +10,7 @@ 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 @@ protected function execute(InputInterface $input, OutputInterface $output): int break; } + $this->appConfig->setValueString('core', 'taskprocessing_worker_last_iteration', (string)time(), lazy: true); $processedTask = $this->processNextTask($output, $taskTypes); if ($once) { From 0325e20a6b0468580316ab65e6cb47d0d0b28548 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 11:44:51 +0200 Subject: [PATCH 02/10] fix(TaskProcessing): Make setup checks more sensitive Signed-off-by: Marcel Klehr --- apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php | 2 +- apps/settings/lib/SetupChecks/TaskProcessingSuccessRate.php | 4 ++-- .../tests/SetupChecks/TaskProcessingPickupSpeedTest.php | 4 ++-- .../tests/SetupChecks/TaskProcessingSuccessRateTest.php | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php index 4897d0cf2de0f..4b7c3a39f9d25 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php @@ -16,7 +16,7 @@ use OCP\TaskProcessing\IManager; class TaskProcessingPickupSpeed implements ISetupCheck { - public const MAX_SLOW_PERCENTAGE = 0.2; + public const MAX_SLOW_PERCENTAGE = 0.1; public const MAX_DAYS = 14; diff --git a/apps/settings/lib/SetupChecks/TaskProcessingSuccessRate.php b/apps/settings/lib/SetupChecks/TaskProcessingSuccessRate.php index 4fc801ad8b7ef..cde34bcf543e4 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingSuccessRate.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingSuccessRate.php @@ -17,7 +17,7 @@ use OCP\TaskProcessing\Task; class TaskProcessingSuccessRate implements ISetupCheck { - public const MAX_FAILURE_PERCENTAGE = 0.2; + public const MAX_FAILURE_PERCENTAGE = 0.1; public const MAX_DAYS = 14; @@ -33,7 +33,7 @@ public function getCategory(): string { } public function getName(): string { - return $this->l10n->t('Task Processing pickup speed'); + return $this->l10n->t('Task Processing success rate'); } public function run(): SetupResult { diff --git a/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php index 6375d9f6e7fa2..0bb3b0d05878b 100644 --- a/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php +++ b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php @@ -42,8 +42,8 @@ public function testPass(): void { for ($i = 0; $i < 100; $i++) { $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); $task->setStartedAt(0); - if ($i < 15) { - $task->setScheduledAt(60 * 5); // 15% get 5mins + if ($i < 5) { + $task->setScheduledAt(60 * 5); // 5% get 5mins } else { $task->setScheduledAt(60); // the rest gets 1min } diff --git a/apps/settings/tests/SetupChecks/TaskProcessingSuccessRateTest.php b/apps/settings/tests/SetupChecks/TaskProcessingSuccessRateTest.php index 2c8b850ff6ae8..56733a9a95810 100644 --- a/apps/settings/tests/SetupChecks/TaskProcessingSuccessRateTest.php +++ b/apps/settings/tests/SetupChecks/TaskProcessingSuccessRateTest.php @@ -43,8 +43,8 @@ public function testPass(): void { $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); $task->setStartedAt(0); $task->setEndedAt(1); - if ($i < 15) { - $task->setStatus(Task::STATUS_FAILED); // 15% get status FAILED + if ($i < 5) { + $task->setStatus(Task::STATUS_FAILED); // 5% get status FAILED } else { $task->setStatus(Task::STATUS_SUCCESSFUL); } From 66eecb6be986f94914218d804d19d57bf5df209c Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 12:49:35 +0200 Subject: [PATCH 03/10] fix: Register taskprocessing setup checks Signed-off-by: Marcel Klehr --- apps/settings/lib/AppInfo/Application.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 7f837bfd0ae1a..41a50cb870fbf 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -72,6 +72,8 @@ use OCA\Settings\SetupChecks\SupportedDatabase; use OCA\Settings\SetupChecks\SystemIs64bit; use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed; +use OCA\Settings\SetupChecks\TaskProcessingSuccessRate; +use OCA\Settings\SetupChecks\TaskProcessingWorkerIsRunning; use OCA\Settings\SetupChecks\TempSpaceAvailable; use OCA\Settings\SetupChecks\TransactionIsolation; use OCA\Settings\SetupChecks\TwoFactorConfiguration; @@ -212,6 +214,8 @@ public function register(IRegistrationContext $context): void { $context->registerSetupCheck(SupportedDatabase::class); $context->registerSetupCheck(SystemIs64bit::class); $context->registerSetupCheck(TaskProcessingPickupSpeed::class); + $context->registerSetupCheck(TaskProcessingSuccessRate::class); + $context->registerSetupCheck(TaskProcessingWorkerIsRunning::class); $context->registerSetupCheck(TempSpaceAvailable::class); $context->registerSetupCheck(TransactionIsolation::class); $context->registerSetupCheck(TwoFactorConfiguration::class); From 490c20ae1dcf63c99ea50839ccf8447dde3278fb Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 12:51:34 +0200 Subject: [PATCH 04/10] fix: Address review comment Co-authored-by: Julien Veyssier Signed-off-by: Marcel Klehr --- apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php index 0ccc1d3e7ac83..ad977e1c2ec2c 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php @@ -52,7 +52,7 @@ public function run(): SetupResult { ); } $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) { + 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) ); From 1a724c977cc6ea88bf7198e04958f70e7e9d4771 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 13:00:48 +0200 Subject: [PATCH 05/10] fix: Fix taskprocessing:worker command tests Signed-off-by: Marcel Klehr --- tests/Core/Command/TaskProcessing/WorkerCommandTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php index 3f472548cef49..70fa2d46d8de7 100644 --- a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php +++ b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php @@ -10,6 +10,7 @@ namespace Tests\Core\Command\TaskProcessing; use OC\Core\Command\TaskProcessing\WorkerCommand; +use OCP\IAppConfig; use OCP\TaskProcessing\Exception\Exception; use OCP\TaskProcessing\Exception\NotFoundException; use OCP\TaskProcessing\IManager; @@ -24,6 +25,7 @@ class WorkerCommandTest extends TestCase { private IManager&MockObject $manager; private LoggerInterface&MockObject $logger; + private IAppConfig&MockObject $appConfig; private WorkerCommand $command; protected function setUp(): void { @@ -31,7 +33,8 @@ protected function setUp(): void { $this->manager = $this->createMock(IManager::class); $this->logger = $this->createMock(LoggerInterface::class); - $this->command = new WorkerCommand($this->manager, $this->logger); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->command = new WorkerCommand($this->manager, $this->logger, $this->appConfig); } /** From d086af533dfb12b9c9962cc7e0068dafbb3686cf Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 15:18:06 +0200 Subject: [PATCH 06/10] fix(TaskProcessingWorkerIsRunning): Different message if the worker has never run Signed-off-by: Marcel Klehr --- .../lib/SetupChecks/TaskProcessingWorkerIsRunning.php | 11 +++++++++-- .../SetupChecks/TaskProcessingWorkerIsRunningTest.php | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php index ad977e1c2ec2c..c3ab5c8845094 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php @@ -54,11 +54,18 @@ public function run(): SetupResult { $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) + $this->l10n->n('The Task Processing worker has run in the last minute.', 'The Task Processing worker has run in the last %n minutes.', self::IS_RUNNING_IN_LAST_X_MINUTES) ); } + + if ($lastIteration > 0) { + 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', $lastIteration)]) + ); + } + 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'))]) + $this->l10n->t('The Task Processing worker does not seem to be running. It seems it has never run so far.') ); } } diff --git a/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php b/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php index 61b9dd95932a2..43df9e599b686 100644 --- a/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php +++ b/apps/settings/tests/SetupChecks/TaskProcessingWorkerIsRunningTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests; @@ -53,7 +53,7 @@ public function testPass(): void { $tasks[] = $task; } $this->taskProcessingManager->method('getTasks')->willReturn($tasks); - + $this->timeFactory->method('now')->willReturn(new \DateTimeImmutable()); $this->appConfig->method('getValueString')->willReturn((string)$this->timeFactory->now()->getTimestamp()); $this->assertEquals(SetupResult::SUCCESS, $this->check->run()->getSeverity()); @@ -70,7 +70,7 @@ public function testFail(): void { $tasks[] = $task; } $this->taskProcessingManager->method('getTasks')->willReturn($tasks); - + $this->timeFactory->method('now')->willReturn(new \DateTimeImmutable()); $this->appConfig->method('getValueString')->willReturn((string)($this->timeFactory->now()->getTimestamp() - 60 * 10)); $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); From 4195f4a03dac23e56c57049c0290c961ae870a05 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 28 Apr 2026 15:33:22 +0200 Subject: [PATCH 07/10] fix(TaskProcessingWorker): Only store config value every 60s Signed-off-by: Marcel Klehr --- core/Command/TaskProcessing/WorkerCommand.php | 14 ++++++++++---- .../Command/TaskProcessing/WorkerCommandTest.php | 6 +++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/Command/TaskProcessing/WorkerCommand.php b/core/Command/TaskProcessing/WorkerCommand.php index 15719f5245fa1..a5171b36c26ef 100644 --- a/core/Command/TaskProcessing/WorkerCommand.php +++ b/core/Command/TaskProcessing/WorkerCommand.php @@ -10,6 +10,7 @@ use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\IAppConfig; use OCP\TaskProcessing\Exception\Exception; use OCP\TaskProcessing\Exception\NotFoundException; @@ -25,6 +26,7 @@ public function __construct( private readonly IManager $taskProcessingManager, private readonly LoggerInterface $logger, private readonly IAppConfig $appConfig, + private readonly ITimeFactory $timeFactory, ) { parent::__construct(); } @@ -63,12 +65,13 @@ protected function configure(): void { } protected function execute(InputInterface $input, OutputInterface $output): int { - $startTime = time(); + $startTime = $this->timeFactory->now()->getTimestamp(); $timeout = (int)$input->getOption('timeout'); $interval = (int)$input->getOption('interval'); $once = $input->getOption('once') === true; /** @var list $taskTypes */ $taskTypes = $input->getOption('taskTypes'); + $lastConfigStorageTime = 0; if ($timeout > 0) { $output->writeln('Task processing worker will stop after ' . $timeout . ' seconds'); @@ -76,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int while (true) { // Stop if timeout exceeded - if ($timeout > 0 && ($startTime + $timeout) < time()) { + if ($timeout > 0 && ($startTime + $timeout) < $this->timeFactory->now()->getTimestamp()) { $output->writeln('Timeout reached, exiting...', OutputInterface::VERBOSITY_VERBOSE); break; } @@ -84,12 +87,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Handle SIGTERM/SIGINT gracefully try { $this->abortIfInterrupted(); - } catch (InterruptedException $e) { + } catch (InterruptedException) { $output->writeln('Task processing worker stopped'); break; } - $this->appConfig->setValueString('core', 'taskprocessing_worker_last_iteration', (string)time(), lazy: true); + if ($lastConfigStorageTime < $this->timeFactory->now()->getTimestamp() - 60) { + $this->appConfig->setValueString('core', 'taskprocessing_worker_last_iteration', (string)$this->timeFactory->now()->getTimestamp(), lazy: true); + $lastConfigStorageTime = $this->timeFactory->now()->getTimestamp(); + } $processedTask = $this->processNextTask($output, $taskTypes); if ($once) { diff --git a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php index 70fa2d46d8de7..0e41f15682f0b 100644 --- a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php +++ b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php @@ -10,6 +10,7 @@ namespace Tests\Core\Command\TaskProcessing; use OC\Core\Command\TaskProcessing\WorkerCommand; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\IAppConfig; use OCP\TaskProcessing\Exception\Exception; use OCP\TaskProcessing\Exception\NotFoundException; @@ -26,6 +27,7 @@ class WorkerCommandTest extends TestCase { private IManager&MockObject $manager; private LoggerInterface&MockObject $logger; private IAppConfig&MockObject $appConfig; + private ITimeFactory&MockObject $timeFactory; private WorkerCommand $command; protected function setUp(): void { @@ -34,7 +36,9 @@ protected function setUp(): void { $this->manager = $this->createMock(IManager::class); $this->logger = $this->createMock(LoggerInterface::class); $this->appConfig = $this->createMock(IAppConfig::class); - $this->command = new WorkerCommand($this->manager, $this->logger, $this->appConfig); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->timeFactory->method('now')->willReturnCallback(fn() => new \DateTimeImmutable()); + $this->command = new WorkerCommand($this->manager, $this->logger, $this->appConfig, $this->timeFactory); } /** From d8cc45b0a7b809c449df3202689058b61eace113 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 29 Apr 2026 10:11:25 +0200 Subject: [PATCH 08/10] fix(TaskProcessingWorker): Adjust config key name Signed-off-by: Marcel Klehr --- apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php | 2 +- core/Command/TaskProcessing/WorkerCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php index c3ab5c8845094..e0c1f06bae91f 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php @@ -51,7 +51,7 @@ public function run(): SetupResult { ) ); } - $lastIteration = (int)$this->appConfig->getValueString('core', 'taskprocessing_worker_last_iteration', lazy: true); + $lastIteration = (int)$this->appConfig->getValueString('core', 'ai.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 minutes.', self::IS_RUNNING_IN_LAST_X_MINUTES) diff --git a/core/Command/TaskProcessing/WorkerCommand.php b/core/Command/TaskProcessing/WorkerCommand.php index a5171b36c26ef..b8df7aea5c25e 100644 --- a/core/Command/TaskProcessing/WorkerCommand.php +++ b/core/Command/TaskProcessing/WorkerCommand.php @@ -93,7 +93,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($lastConfigStorageTime < $this->timeFactory->now()->getTimestamp() - 60) { - $this->appConfig->setValueString('core', 'taskprocessing_worker_last_iteration', (string)$this->timeFactory->now()->getTimestamp(), lazy: true); + $this->appConfig->setValueString('core', 'ai.taskprocessing_worker_last_iteration', (string)$this->timeFactory->now()->getTimestamp(), lazy: true); $lastConfigStorageTime = $this->timeFactory->now()->getTimestamp(); } $processedTask = $this->processNextTask($output, $taskTypes); From 10c2a9d2e98904d158de912756f879628398c2a9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 29 Apr 2026 10:16:02 +0200 Subject: [PATCH 09/10] fix: Run cs:fix Signed-off-by: Marcel Klehr --- tests/Core/Command/TaskProcessing/WorkerCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php index 0e41f15682f0b..a83418338d12c 100644 --- a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php +++ b/tests/Core/Command/TaskProcessing/WorkerCommandTest.php @@ -37,7 +37,7 @@ protected function setUp(): void { $this->logger = $this->createMock(LoggerInterface::class); $this->appConfig = $this->createMock(IAppConfig::class); $this->timeFactory = $this->createMock(ITimeFactory::class); - $this->timeFactory->method('now')->willReturnCallback(fn() => new \DateTimeImmutable()); + $this->timeFactory->method('now')->willReturnCallback(fn () => new \DateTimeImmutable()); $this->command = new WorkerCommand($this->manager, $this->logger, $this->appConfig, $this->timeFactory); } From 98a1f1b4d1bb2ecac2eefd67e54bdd4645f8c76f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 29 Apr 2026 10:59:32 +0200 Subject: [PATCH 10/10] fix: Fix psalm issues Signed-off-by: Marcel Klehr --- .../settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php index e0c1f06bae91f..e480d6765c955 100644 --- a/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php +++ b/apps/settings/lib/SetupChecks/TaskProcessingWorkerIsRunning.php @@ -29,14 +29,17 @@ public function __construct( ) { } + #[\Override] public function getCategory(): string { return 'ai'; } + #[\Override] public function getName(): string { return $this->l10n->t('Task Processing worker status'); } + #[\Override] 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));