feat(taskprocessing): Add queue_stats API endpoint for external autoscalers

This commit is contained in:
Oleksander Piskun 2026-02-17 13:18:08 +02:00
parent b0e99d0293
commit eed2cdd5b7
4 changed files with 75 additions and 0 deletions

View file

@ -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<Http::STATUS_OK, array{scheduled: int, running: int}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}>
*
* 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
*

View file

@ -265,6 +265,26 @@ class TaskMapper extends QBMapper {
return $this->findEntities($qb);
}
/**
* @param list<string> $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
*/

View file

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

View file

@ -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<string> $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
*