diff --git a/core/Controller/TaskProcessingApiController.php b/core/Controller/TaskProcessingApiController.php index 5f2023dd0c2..6b2f2664762 100644 --- a/core/Controller/TaskProcessingApiController.php +++ b/core/Controller/TaskProcessingApiController.php @@ -424,6 +424,42 @@ class TaskProcessingApiController extends OCSController { } } + /** + * Returns queue statistics for task processing + * + * Returns the count of scheduled and running tasks, optionally filtered + * by task type(s). Designed for external scalers (e.g. KEDA) to poll + * for task queue depth. Admin-only endpoint authenticated via app_password. + * + * @param string|null $taskTypeId Comma-separated list of task type IDs to filter by + * @return DataResponse|DataResponse + * + * 200: Queue stats returned + */ + #[NoCSRFRequired] + #[ApiRoute(verb: 'GET', url: '/queue_stats', root: '/taskprocessing')] + public function queueStats(?string $taskTypeId = null): DataResponse { + # TODO: bruteforce protection? + try { + $taskTypeIds = []; + if ($taskTypeId !== null) { + $taskTypeIds = array_map('trim', explode(',', $taskTypeId)); + $taskTypeIds = array_filter($taskTypeIds, fn (string $id) => $id !== ''); + $taskTypeIds = array_values($taskTypeIds); + } + + $scheduled = $this->taskProcessingManager->countTasks(Task::STATUS_SCHEDULED, $taskTypeIds); + $running = $this->taskProcessingManager->countTasks(Task::STATUS_RUNNING, $taskTypeIds); + + return new DataResponse([ + 'scheduled' => $scheduled, + 'running' => $running, + ]); + } catch (Exception) { + return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); + } + } + /** * Returns the contents of a file referenced in a task * diff --git a/lib/private/TaskProcessing/Db/TaskMapper.php b/lib/private/TaskProcessing/Db/TaskMapper.php index f62bb41be3b..34862e234e2 100644 --- a/lib/private/TaskProcessing/Db/TaskMapper.php +++ b/lib/private/TaskProcessing/Db/TaskMapper.php @@ -265,6 +265,26 @@ class TaskMapper extends QBMapper { return $this->findEntities($qb); } + /** + * @param list $taskTypeIds + * @param int $status + * @return int + * @throws Exception + */ + public function countByStatus(array $taskTypeIds, int $status): int { + $qb = $this->db->getQueryBuilder(); + $qb->select($qb->func()->count('id')) + ->from($this->tableName) + ->where($qb->expr()->eq('status', $qb->createNamedParameter($status, IQueryBuilder::PARAM_INT))); + if (!empty($taskTypeIds)) { + $qb->andWhere($qb->expr()->in('type', $qb->createNamedParameter($taskTypeIds, IQueryBuilder::PARAM_STR_ARRAY))); + } + $result = $qb->executeQuery(); + $count = (int)$result->fetchOne(); + $result->closeCursor(); + return $count; + } + /** * @throws Exception */ diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 884499d34b0..ef8ece9c823 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -1357,6 +1357,14 @@ class Manager implements IManager { } } + public function countTasks(int $status, array $taskTypeIds = []): int { + try { + return $this->taskMapper->countByStatus($taskTypeIds, $status); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem counting the tasks', 0, $e); + } + } + public function getUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array { try { $taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $customId); diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php index 878acfc134c..2cd0244b52e 100644 --- a/lib/public/TaskProcessing/IManager.php +++ b/lib/public/TaskProcessing/IManager.php @@ -258,6 +258,17 @@ interface IManager { */ public function setTaskStatus(Task $task, int $status): void; + /** + * Get the count of tasks filtered by status and optionally by task type(s) + * + * @param int $status The task status to filter by + * @param list $taskTypeIds Optional list of task type IDs to filter by + * @return int The count of matching tasks + * @throws Exception If the query failed + * @since 34.0.0 + */ + public function countTasks(int $status, array $taskTypeIds = []): int; + /** * Extract all input and output file IDs from a task *