aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJulien Veyssier <julien-nc@posteo.net>2024-07-15 13:30:59 +0200
committerJulien Veyssier <julien-nc@posteo.net>2024-07-23 17:12:38 +0200
commitc120a64ba2031817113a0194fd6f2337a4dc420b (patch)
treea3c9734e2dcc5a86502a2768456b9ce34c556b3c /core
parentdf086a8c207ec6765a94955f6638fa7aacf4c06a (diff)
downloadnextcloud-server-c120a64ba2031817113a0194fd6f2337a4dc420b.tar.gz
nextcloud-server-c120a64ba2031817113a0194fd6f2337a4dc420b.zip
feat(taskprocessing): add occ commands to list tasks and compute stats
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
Diffstat (limited to 'core')
-rw-r--r--core/Command/TaskProcessing/ListCommand.php84
-rw-r--r--core/Command/TaskProcessing/Statistics.php145
-rw-r--r--core/Migrations/Version30000Date20240708160048.php6
-rw-r--r--core/register_command.php3
4 files changed, 235 insertions, 3 deletions
diff --git a/core/Command/TaskProcessing/ListCommand.php b/core/Command/TaskProcessing/ListCommand.php
new file mode 100644
index 00000000000..92897c8b9ea
--- /dev/null
+++ b/core/Command/TaskProcessing/ListCommand.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Core\Command\TaskProcessing;
+
+use OC\Core\Command\Base;
+use OCP\TaskProcessing\IManager;
+use OCP\TaskProcessing\Task;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListCommand extends Base {
+ public function __construct(
+ protected IManager $taskProcessingManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('taskprocessing:task:list')
+ ->setDescription('list tasks')
+ ->addOption(
+ 'userIdFilter',
+ 'u',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one user ID'
+ )
+ ->addOption(
+ 'type',
+ 't',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one task type'
+ )
+ ->addOption(
+ 'customId',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one custom ID'
+ )
+ ->addOption(
+ 'status',
+ 's',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that have a specific status (STATUS_UNKNOWN=0, STATUS_SCHEDULED=1, STATUS_RUNNING=2, STATUS_SUCCESSFUL=3, STATUS_FAILED=4, STATUS_CANCELLED=5)'
+ )
+ ->addOption(
+ 'scheduledAfter',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that were scheduled after a specific date (Unix timestamp)'
+ )
+ ->addOption(
+ 'endedBefore',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that ended before a specific date (Unix timestamp)'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $userIdFilter = $input->getOption('userIdFilter');
+ if ($userIdFilter === null) {
+ $userIdFilter = '';
+ } elseif ($userIdFilter === '') {
+ $userIdFilter = null;
+ }
+ $type = $input->getOption('type');
+ $customId = $input->getOption('customId');
+ $status = $input->getOption('status');
+ $scheduledAfter = $input->getOption('scheduledAfter');
+ $endedBefore = $input->getOption('endedBefore');
+
+ $tasks = $this->taskProcessingManager->getTasks($userIdFilter, $type, $customId, $status, $scheduledAfter, $endedBefore);
+ $arrayTasks = array_map(fn (Task $task): array => $task->jsonSerialize(), $tasks);
+
+ $this->writeArrayInOutputFormat($input, $output, $arrayTasks);
+ return 0;
+ }
+}
diff --git a/core/Command/TaskProcessing/Statistics.php b/core/Command/TaskProcessing/Statistics.php
new file mode 100644
index 00000000000..b0e716337f0
--- /dev/null
+++ b/core/Command/TaskProcessing/Statistics.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Core\Command\TaskProcessing;
+
+use OC\Core\Command\Base;
+use OCP\TaskProcessing\IManager;
+use OCP\TaskProcessing\Task;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Statistics extends Base {
+ public function __construct(
+ protected IManager $taskProcessingManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('taskprocessing:task:stats')
+ ->setDescription('get statistics for tasks')
+ ->addOption(
+ 'userIdFilter',
+ 'u',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one user ID'
+ )
+ ->addOption(
+ 'type',
+ 't',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one task type'
+ )
+ ->addOption(
+ 'customId',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks for one custom ID'
+ )
+ ->addOption(
+ 'status',
+ 's',
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that have a specific status (STATUS_UNKNOWN=0, STATUS_SCHEDULED=1, STATUS_RUNNING=2, STATUS_SUCCESSFUL=3, STATUS_FAILED=4, STATUS_CANCELLED=5)'
+ )
+ ->addOption(
+ 'scheduledAfter',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that were scheduled after a specific date (Unix timestamp)'
+ )
+ ->addOption(
+ 'endedBefore',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'only get the tasks that ended before a specific date (Unix timestamp)'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $userIdFilter = $input->getOption('userIdFilter');
+ if ($userIdFilter === null) {
+ $userIdFilter = '';
+ } elseif ($userIdFilter === '') {
+ $userIdFilter = null;
+ }
+ $type = $input->getOption('type');
+ $customId = $input->getOption('customId');
+ $status = $input->getOption('status');
+ $scheduledAfter = $input->getOption('scheduledAfter');
+ $endedBefore = $input->getOption('endedBefore');
+
+ $tasks = $this->taskProcessingManager->getTasks($userIdFilter, $type, $customId, $status, $scheduledAfter, $endedBefore);
+
+ $stats = ['Number of tasks' => count($tasks)];
+
+ $maxRunningTime = 0;
+ $totalRunningTime = 0;
+ $runningTimeCount = 0;
+
+ $maxQueuingTime = 0;
+ $totalQueuingTime = 0;
+ $queuingTimeCount = 0;
+
+ $maxUserWaitingTime = 0;
+ $totalUserWaitingTime = 0;
+ $userWaitingTimeCount = 0;
+ foreach ($tasks as $task) {
+ // running time
+ if ($task->getStartedAt() !== null && $task->getEndedAt() !== null) {
+ $taskRunningTime = $task->getEndedAt() - $task->getStartedAt();
+ $totalRunningTime += $taskRunningTime;
+ $runningTimeCount++;
+ if ($taskRunningTime >= $maxRunningTime) {
+ $maxRunningTime = $taskRunningTime;
+ }
+ }
+ // queuing time
+ if ($task->getScheduledAt() !== null && $task->getStartedAt() !== null) {
+ $taskQueuingTime = $task->getStartedAt() - $task->getScheduledAt();
+ $totalQueuingTime += $taskQueuingTime;
+ $queuingTimeCount++;
+ if ($taskQueuingTime >= $maxQueuingTime) {
+ $maxQueuingTime = $taskQueuingTime;
+ }
+ }
+ // user waiting time
+ if ($task->getScheduledAt() !== null && $task->getEndedAt() !== null) {
+ $taskUserWaitingTime = $task->getEndedAt() - $task->getScheduledAt();
+ $totalUserWaitingTime += $taskUserWaitingTime;
+ $userWaitingTimeCount++;
+ if ($taskUserWaitingTime >= $maxUserWaitingTime) {
+ $maxUserWaitingTime = $taskUserWaitingTime;
+ }
+ }
+ }
+
+ if ($runningTimeCount > 0) {
+ $stats['Max running time'] = $maxRunningTime;
+ $averageRunningTime = (int)($totalRunningTime / $runningTimeCount);
+ $stats['Average running time'] = $averageRunningTime;
+ $stats['Running time count'] = $runningTimeCount;
+ }
+ if ($queuingTimeCount > 0) {
+ $stats['Max queuing time'] = $maxQueuingTime;
+ $averageQueuingTime = (int)($totalQueuingTime / $queuingTimeCount);
+ $stats['Average queuing time'] = $averageQueuingTime;
+ $stats['Queuing time count'] = $queuingTimeCount;
+ }
+ if ($userWaitingTimeCount > 0) {
+ $stats['Max user waiting time'] = $maxUserWaitingTime;
+ $averageUserWaitingTime = (int)($totalUserWaitingTime / $userWaitingTimeCount);
+ $stats['Average user waiting time'] = $averageUserWaitingTime;
+ $stats['User waiting time count'] = $userWaitingTimeCount;
+ }
+
+ $this->writeArrayInOutputFormat($input, $output, $stats);
+ return 0;
+ }
+}
diff --git a/core/Migrations/Version30000Date20240708160048.php b/core/Migrations/Version30000Date20240708160048.php
index a85eea2c974..0b5596a05a9 100644
--- a/core/Migrations/Version30000Date20240708160048.php
+++ b/core/Migrations/Version30000Date20240708160048.php
@@ -34,17 +34,17 @@ class Version30000Date20240708160048 extends SimpleMigrationStep {
$table->addColumn('scheduled_at', Types::INTEGER, [
'notnull' => false,
- 'default' => 0,
+ 'default' => null,
'unsigned' => true,
]);
$table->addColumn('started_at', Types::INTEGER, [
'notnull' => false,
- 'default' => 0,
+ 'default' => null,
'unsigned' => true,
]);
$table->addColumn('ended_at', Types::INTEGER, [
'notnull' => false,
- 'default' => 0,
+ 'default' => null,
'unsigned' => true,
]);
diff --git a/core/register_command.php b/core/register_command.php
index 6560f63d797..6e89568cf9b 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -140,6 +140,9 @@ if ($config->getSystemValueBool('installed', false)) {
$application->add(Server::get(Command\Security\BruteforceResetAttempts::class));
$application->add(Server::get(Command\SetupChecks::class));
$application->add(Server::get(Command\FilesMetadata\Get::class));
+
+ $application->add(Server::get(Command\TaskProcessing\ListCommand::class));
+ $application->add(Server::get(Command\TaskProcessing\Statistics::class));
} else {
$application->add(Server::get(Command\Maintenance\Install::class));
}