feat(TaskProcessing): Add setup check for task processing worker status

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
Marcel Klehr 2026-04-28 11:43:03 +02:00 committed by backportbot[bot]
parent ac1fc334b8
commit fa14e6a2c0
5 changed files with 147 additions and 0 deletions

View file

@ -138,6 +138,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',

View file

@ -153,6 +153,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',

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\SetupChecks;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\SetupCheck\ISetupCheck;
use OCP\SetupCheck\SetupResult;
use OCP\TaskProcessing\IManager;
class TaskProcessingWorkerIsRunning implements ISetupCheck {
public const HAS_TASKS_IN_LAST_X_DAYS = 7;
public const IS_RUNNING_IN_LAST_X_MINUTES = 5;
public function __construct(
private readonly IL10N $l10n,
private readonly IManager $taskProcessingManager,
private readonly ITimeFactory $timeFactory,
private readonly IAppConfig $appConfig,
) {
}
public function getCategory(): string {
return 'ai';
}
public function getName(): string {
return $this->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'))])
);
}
}

View file

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Tests;
use OCA\Settings\SetupChecks\TaskProcessingWorkerIsRunning;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\SetupCheck\SetupResult;
use OCP\TaskProcessing\IManager;
use OCP\TaskProcessing\Task;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class TaskProcessingWorkerIsRunningTest extends TestCase {
private IL10N&MockObject $l10n;
private ITimeFactory&MockObject $timeFactory;
private IManager&MockObject $taskProcessingManager;
private IAppConfig&MockObject $appConfig;
private TaskProcessingWorkerIsRunning $check;
protected function setUp(): void {
parent::setUp();
$this->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());
}
}

View file

@ -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) {