mirror of
https://github.com/nextcloud/server.git
synced 2026-06-10 17:23:59 -04:00
Merge pull request #58376 from nextcloud/feat/taskprocessing/keda-autoscaler
feat(taskprocessing): Add queue_stats API endpoint for external autoscalers
This commit is contained in:
commit
5ce08e9704
7 changed files with 627 additions and 0 deletions
|
|
@ -424,6 +424,34 @@ 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 list<string> $taskTypeIds List of task type IDs to filter by
|
||||
* @return DataResponse<Http::STATUS_OK, array{scheduled_count: int, running_count: 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(array $taskTypeIds = []): DataResponse {
|
||||
try {
|
||||
$scheduled = $this->taskProcessingManager->countTasks(Task::STATUS_SCHEDULED, $taskTypeIds);
|
||||
$running = $this->taskProcessingManager->countTasks(Task::STATUS_RUNNING, $taskTypeIds);
|
||||
|
||||
return new DataResponse([
|
||||
'scheduled_count' => $scheduled,
|
||||
'running_count' => $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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -129,6 +129,188 @@
|
|||
}
|
||||
},
|
||||
"paths": {
|
||||
"/ocs/v2.php/taskprocessing/queue_stats": {
|
||||
"get": {
|
||||
"operationId": "task_processing_api-queue-stats",
|
||||
"summary": "Returns queue statistics for task processing",
|
||||
"description": "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.\nThis endpoint requires admin access",
|
||||
"tags": [
|
||||
"task_processing_api"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "taskTypeIds[]",
|
||||
"in": "query",
|
||||
"description": "List of task type IDs to filter by",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Queue stats returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"scheduled_count",
|
||||
"running_count"
|
||||
],
|
||||
"properties": {
|
||||
"scheduled_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"running_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Logged in account must be an admin",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/twofactor/state": {
|
||||
"get": {
|
||||
"operationId": "two_factor_api-state",
|
||||
|
|
|
|||
|
|
@ -12649,6 +12649,188 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/taskprocessing/queue_stats": {
|
||||
"get": {
|
||||
"operationId": "task_processing_api-queue-stats",
|
||||
"summary": "Returns queue statistics for task processing",
|
||||
"description": "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.\nThis endpoint requires admin access",
|
||||
"tags": [
|
||||
"task_processing_api"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "taskTypeIds[]",
|
||||
"in": "query",
|
||||
"description": "List of task type IDs to filter by",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Queue stats returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"scheduled_count",
|
||||
"running_count"
|
||||
],
|
||||
"properties": {
|
||||
"scheduled_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"running_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Logged in account must be an admin",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/twofactor/state": {
|
||||
"get": {
|
||||
"operationId": "two_factor_api-state",
|
||||
|
|
|
|||
|
|
@ -265,6 +265,40 @@ 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 {
|
||||
if ($taskTypeIds === []) {
|
||||
return $this->countByStatusQuery($status);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach (array_chunk($taskTypeIds, 900) as $chunk) {
|
||||
$count += $this->countByStatusQuery($status, $chunk);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function countByStatusQuery(int $status, ?array $taskTypeIds = null): 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 ($taskTypeIds !== null) {
|
||||
$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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
182
openapi.json
182
openapi.json
|
|
@ -16351,6 +16351,188 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/taskprocessing/queue_stats": {
|
||||
"get": {
|
||||
"operationId": "core-task_processing_api-queue-stats",
|
||||
"summary": "Returns queue statistics for task processing",
|
||||
"description": "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.\nThis endpoint requires admin access",
|
||||
"tags": [
|
||||
"core/task_processing_api"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "taskTypeIds[]",
|
||||
"in": "query",
|
||||
"description": "List of task type IDs to filter by",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Queue stats returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"scheduled_count",
|
||||
"running_count"
|
||||
],
|
||||
"properties": {
|
||||
"scheduled_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"running_count": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Logged in account must be an admin",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/twofactor/state": {
|
||||
"get": {
|
||||
"operationId": "core-two_factor_api-state",
|
||||
|
|
|
|||
Loading…
Reference in a new issue