mirror of
https://github.com/nextcloud/server.git
synced 2026-02-19 02:38:40 -05:00
feat(TaskProcessing): Introduce ITriggerableProvider
Signed-off-by: Marcel Klehr <mklehr@gmx.net> # Conflicts: # lib/private/TaskProcessing/Db/TaskMapper.php
This commit is contained in:
parent
4c17229789
commit
044e92bf4e
6 changed files with 149 additions and 6 deletions
|
|
@ -872,6 +872,7 @@ return array(
|
|||
'OCP\\TaskProcessing\\IProvider' => $baseDir . '/lib/public/TaskProcessing/IProvider.php',
|
||||
'OCP\\TaskProcessing\\ISynchronousProvider' => $baseDir . '/lib/public/TaskProcessing/ISynchronousProvider.php',
|
||||
'OCP\\TaskProcessing\\ITaskType' => $baseDir . '/lib/public/TaskProcessing/ITaskType.php',
|
||||
'OCP\\TaskProcessing\\ITriggerableProvider' => $baseDir . '/lib/public/TaskProcessing/ITriggerableProvider.php',
|
||||
'OCP\\TaskProcessing\\ShapeDescriptor' => $baseDir . '/lib/public/TaskProcessing/ShapeDescriptor.php',
|
||||
'OCP\\TaskProcessing\\ShapeEnumValue' => $baseDir . '/lib/public/TaskProcessing/ShapeEnumValue.php',
|
||||
'OCP\\TaskProcessing\\Task' => $baseDir . '/lib/public/TaskProcessing/Task.php',
|
||||
|
|
|
|||
|
|
@ -11,32 +11,32 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'O' =>
|
||||
'O' =>
|
||||
array (
|
||||
'OC\\Core\\' => 8,
|
||||
'OC\\' => 3,
|
||||
'OCP\\' => 4,
|
||||
),
|
||||
'N' =>
|
||||
'N' =>
|
||||
array (
|
||||
'NCU\\' => 4,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'OC\\Core\\' =>
|
||||
'OC\\Core\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../../..' . '/core',
|
||||
),
|
||||
'OC\\' =>
|
||||
'OC\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../../..' . '/lib/private',
|
||||
),
|
||||
'OCP\\' =>
|
||||
'OCP\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../../..' . '/lib/public',
|
||||
),
|
||||
'NCU\\' =>
|
||||
'NCU\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../../..' . '/lib/unstable',
|
||||
),
|
||||
|
|
@ -913,6 +913,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OCP\\TaskProcessing\\IProvider' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/IProvider.php',
|
||||
'OCP\\TaskProcessing\\ISynchronousProvider' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ISynchronousProvider.php',
|
||||
'OCP\\TaskProcessing\\ITaskType' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ITaskType.php',
|
||||
'OCP\\TaskProcessing\\ITriggerableProvider' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ITriggerableProvider.php',
|
||||
'OCP\\TaskProcessing\\ShapeDescriptor' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ShapeDescriptor.php',
|
||||
'OCP\\TaskProcessing\\ShapeEnumValue' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ShapeEnumValue.php',
|
||||
'OCP\\TaskProcessing\\Task' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/Task.php',
|
||||
|
|
|
|||
|
|
@ -264,4 +264,20 @@ class TaskMapper extends QBMapper {
|
|||
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function hasRunningTasksForTaskType(string $getTaskTypeId): bool {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
->from($this->tableName);
|
||||
$qb->where($qb->expr()->eq('type', $qb->createNamedParameter($getTaskTypeId)));
|
||||
$qb->andWhere($qb->expr()->eq('status', $qb->createNamedParameter(\OCP\TaskProcessing\Task::STATUS_RUNNING, IQueryBuilder::PARAM_INT)));
|
||||
$qb->setMaxResults(1);
|
||||
$result = $qb->executeQuery();
|
||||
$hasRunningTasks = $result->fetch() !== false;
|
||||
$result->closeCursor();
|
||||
return $hasRunningTasks;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ use OCP\TaskProcessing\IManager;
|
|||
use OCP\TaskProcessing\IProvider;
|
||||
use OCP\TaskProcessing\ISynchronousProvider;
|
||||
use OCP\TaskProcessing\ITaskType;
|
||||
use OCP\TaskProcessing\ITriggerableProvider;
|
||||
use OCP\TaskProcessing\ShapeDescriptor;
|
||||
use OCP\TaskProcessing\ShapeEnumValue;
|
||||
use OCP\TaskProcessing\Task;
|
||||
|
|
@ -976,6 +977,16 @@ class Manager implements IManager {
|
|||
if ($provider instanceof ISynchronousProvider) {
|
||||
$this->jobList->add(SynchronousBackgroundJob::class, null);
|
||||
}
|
||||
if ($provider instanceof ITriggerableProvider) {
|
||||
try {
|
||||
if (!$this->taskMapper->hasRunningTasksForTaskType($task->getTaskTypeId())) {
|
||||
// If no tasks are currently running for this task type, nudge the provider to ask for tasks
|
||||
$provider->trigger();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('Failed to check DB for running tasks after a task was scheduled for a triggerable provider. Not triggering the provider.', ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function runTask(Task $task): Task {
|
||||
|
|
|
|||
27
lib/public/TaskProcessing/ITriggerableProvider.php
Normal file
27
lib/public/TaskProcessing/ITriggerableProvider.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
namespace OCP\TaskProcessing;
|
||||
|
||||
/**
|
||||
* This is the interface that is implemented by apps that
|
||||
* implement a task processing provider with support for being triggered
|
||||
* @since 33.0.0
|
||||
*/
|
||||
interface ITriggerableProvider extends IProvider {
|
||||
|
||||
/**
|
||||
* Called when new tasks for this provider are coming in and there are currently
|
||||
* no tasks running for this provider's task type
|
||||
*
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function trigger(): void;
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ use OCP\TaskProcessing\IManager;
|
|||
use OCP\TaskProcessing\IProvider;
|
||||
use OCP\TaskProcessing\ISynchronousProvider;
|
||||
use OCP\TaskProcessing\ITaskType;
|
||||
use OCP\TaskProcessing\ITriggerableProvider;
|
||||
use OCP\TaskProcessing\ShapeDescriptor;
|
||||
use OCP\TaskProcessing\Task;
|
||||
use OCP\TaskProcessing\TaskTypes\TextToImage;
|
||||
|
|
@ -438,6 +439,53 @@ class ExternalProvider implements IProvider {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class ExternalTriggerableProvider implements ITriggerableProvider {
|
||||
public const ID = 'event:external:provider:triggerable';
|
||||
public const TASK_TYPE_ID = TextToText::ID;
|
||||
|
||||
public function getId(): string {
|
||||
return self::ID;
|
||||
}
|
||||
public function getName(): string {
|
||||
return 'External Triggerable Provider via Event';
|
||||
}
|
||||
|
||||
public function getTaskTypeId(): string {
|
||||
return self::TASK_TYPE_ID;
|
||||
}
|
||||
|
||||
public function trigger(): void {
|
||||
}
|
||||
public function getExpectedRuntime(): int {
|
||||
return 5;
|
||||
}
|
||||
public function getOptionalInputShape(): array {
|
||||
return [];
|
||||
}
|
||||
public function getOptionalOutputShape(): array {
|
||||
return [];
|
||||
}
|
||||
public function getInputShapeEnumValues(): array {
|
||||
return [];
|
||||
}
|
||||
public function getInputShapeDefaults(): array {
|
||||
return [];
|
||||
}
|
||||
public function getOptionalInputShapeEnumValues(): array {
|
||||
return [];
|
||||
}
|
||||
public function getOptionalInputShapeDefaults(): array {
|
||||
return [];
|
||||
}
|
||||
public function getOutputShapeEnumValues(): array {
|
||||
return [];
|
||||
}
|
||||
public function getOptionalOutputShapeEnumValues(): array {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class ConflictingExternalProvider implements IProvider {
|
||||
// Same ID as SuccessfulSyncProvider
|
||||
public const ID = 'test:sync:success';
|
||||
|
|
@ -555,6 +603,7 @@ class TaskProcessingTest extends \Test\TestCase {
|
|||
SuccessfulTextToImageProvider::class => new SuccessfulTextToImageProvider(),
|
||||
FailingTextToImageProvider::class => new FailingTextToImageProvider(),
|
||||
ExternalProvider::class => new ExternalProvider(),
|
||||
ExternalTriggerableProvider::class => new ExternalTriggerableProvider(),
|
||||
ConflictingExternalProvider::class => new ConflictingExternalProvider(),
|
||||
ExternalTaskType::class => new ExternalTaskType(),
|
||||
ConflictingExternalTaskType::class => new ConflictingExternalTaskType(),
|
||||
|
|
@ -1227,6 +1276,44 @@ class TaskProcessingTest extends \Test\TestCase {
|
|||
self::assertCount(1, $providers); // Ensure no extra provider was added
|
||||
}
|
||||
|
||||
public function testTriggerableProviderWithNoOtherRunningTasks() {
|
||||
// Arrange: Local provider registered, conflicting external provider via event
|
||||
$this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
|
||||
|
||||
$externalProvider = $this->createPartialMock(ExternalTriggerableProvider::class, ['trigger']);
|
||||
$externalProvider->expects($this->once())->method('trigger');
|
||||
$this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
|
||||
$this->manager = $this->createManagerInstance();
|
||||
|
||||
// Act
|
||||
$task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', 'foobar');
|
||||
$this->manager->scheduleTask($task);
|
||||
}
|
||||
|
||||
public function testTriggerableProviderWithOtherRunningTasks() {
|
||||
// Arrange: Local provider registered, conflicting external provider via event
|
||||
$this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
|
||||
$this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
|
||||
|
||||
$externalProvider = $this->createPartialMock(ExternalTriggerableProvider::class, ['trigger']);
|
||||
$externalProvider->expects($this->once())->method('trigger');
|
||||
$this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
|
||||
$this->manager = $this->createManagerInstance();
|
||||
|
||||
$task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', 'foobar');
|
||||
$this->manager->scheduleTask($task);
|
||||
$this->manager->lockTask($task);
|
||||
|
||||
// Act
|
||||
$task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', 'foobar');
|
||||
$this->manager->scheduleTask($task);
|
||||
}
|
||||
|
||||
public function testMergeTaskTypesLocalAndEvent() {
|
||||
// Arrange: Local type registered, DIFFERENT external type via event
|
||||
$this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
|
||||
|
|
|
|||
Loading…
Reference in a new issue