diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 968953f52ee6c..8732881f9966c 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -128,6 +128,7 @@ 'OCA\\Settings\\SetupChecks\\SecurityHeaders' => $baseDir . '/../lib/SetupChecks/SecurityHeaders.php', 'OCA\\Settings\\SetupChecks\\SupportedDatabase' => $baseDir . '/../lib/SetupChecks/SupportedDatabase.php', 'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php', + 'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => $baseDir . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => $baseDir . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => $baseDir . '/../lib/SetupChecks/TransactionIsolation.php', 'OCA\\Settings\\SetupChecks\\WellKnownUrls' => $baseDir . '/../lib/SetupChecks/WellKnownUrls.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 24a99a8638105..c7358bc249df0 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -143,6 +143,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\SetupChecks\\SecurityHeaders' => __DIR__ . '/..' . '/../lib/SetupChecks/SecurityHeaders.php', 'OCA\\Settings\\SetupChecks\\SupportedDatabase' => __DIR__ . '/..' . '/../lib/SetupChecks/SupportedDatabase.php', 'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php', + 'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => __DIR__ . '/..' . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => __DIR__ . '/..' . '/../lib/SetupChecks/TransactionIsolation.php', 'OCA\\Settings\\SetupChecks\\WellKnownUrls' => __DIR__ . '/..' . '/../lib/SetupChecks/WellKnownUrls.php', diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 2841b181d1ad9..f7abb75a99772 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -70,6 +70,7 @@ use OCA\Settings\SetupChecks\SecurityHeaders; use OCA\Settings\SetupChecks\SupportedDatabase; use OCA\Settings\SetupChecks\SystemIs64bit; +use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed; use OCA\Settings\SetupChecks\TempSpaceAvailable; use OCA\Settings\SetupChecks\TransactionIsolation; use OCA\Settings\SetupChecks\WellKnownUrls; @@ -215,6 +216,7 @@ public function register(IRegistrationContext $context): void { $context->registerSetupCheck(SchedulingTableSize::class); $context->registerSetupCheck(SupportedDatabase::class); $context->registerSetupCheck(SystemIs64bit::class); + $context->registerSetupCheck(TaskProcessingPickupSpeed::class); $context->registerSetupCheck(TempSpaceAvailable::class); $context->registerSetupCheck(TransactionIsolation::class); $context->registerSetupCheck(PushService::class); diff --git a/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php new file mode 100644 index 0000000000000..83168ac0f3e6e --- /dev/null +++ b/apps/settings/lib/SetupChecks/TaskProcessingPickupSpeed.php @@ -0,0 +1,63 @@ +l10n->t('Task Processing pickup speed'); + } + + public function run(): SetupResult { + $tasks = $this->taskProcessingManager->getTasks(userId: '', scheduleAfter: $this->timeFactory->now()->getTimestamp() - 60 * 60 * self::TIME_SPAN); // userId: '' means no filter, whereas null would mean guest + $taskCount = count($tasks); + if ($taskCount === 0) { + return SetupResult::success($this->l10n->n('No scheduled tasks in the last %n hour.', 'No scheduled tasks in the last %n hours.', self::TIME_SPAN)); + } + $slowCount = 0; + foreach ($tasks as $task) { + if ($task->getStartedAt() === null) { + continue; // task was not picked up yet + } + if ($task->getScheduledAt() === null) { + continue; // task was not scheduled yet -- should not happen, but the API specifies null as return value + } + $pickupDelay = $task->getScheduledAt() - $task->getStartedAt(); + if ($pickupDelay > 60 * 4) { + $slowCount++; // task pickup took longer than 4 minutes + } + } + + if ($slowCount / $taskCount < self::MAX_SLOW_PERCENTAGE) { + return SetupResult::success($this->l10n->n('The task pickup speed has been ok in the last %n hour.', 'The task pickup speed has been ok in the last %n hours.', self::TIME_SPAN)); + } else { + return SetupResult::warning($this->l10n->n('The task pickup speed has been slow in the last %n hour. Many tasks took longer than 4 minutes to be picked up. Consider setting up a worker to process tasks in the background.', 'The task pickup speed has been slow in the last %n hours. Many tasks took longer than 4 minutes to be picked up. Consider setting up a worker to process tasks in the background.', self::TIME_SPAN), 'https://docs.nextcloud.com/server/latest/admin_manual/ai/overview.html#improve-ai-task-pickup-speed'); + } + } +} diff --git a/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php new file mode 100644 index 0000000000000..6375d9f6e7fa2 --- /dev/null +++ b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php @@ -0,0 +1,73 @@ +l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->timeFactory = $this->getMockBuilder(ITimeFactory::class)->getMock(); + $this->taskProcessingManager = $this->getMockBuilder(IManager::class)->getMock(); + + $this->check = new TaskProcessingPickupSpeed( + $this->l10n, + $this->taskProcessingManager, + $this->timeFactory, + ); + } + + public function testPass(): void { + $tasks = []; + 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 + } else { + $task->setScheduledAt(60); // the rest gets 1min + } + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->assertEquals(SetupResult::SUCCESS, $this->check->run()->getSeverity()); + } + + public function testFail(): void { + $tasks = []; + for ($i = 0; $i < 100; $i++) { + $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); + $task->setStartedAt(0); + if ($i < 30) { + $task->setScheduledAt(60 * 5); // 30% get 5mins + } else { + $task->setScheduledAt(60); // the rest gets 1min + } + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); + } +}