Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected function setUp(): void {
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRun(int $lastRun, int $time, bool $process): void {
$backgroundJob = new RefreshWebcalJob($this->refreshWebcalService, $this->config, $this->logger, $this->timeFactory);
$backgroundJob->setId(42);
$backgroundJob->setId('42');

$backgroundJob->setArgument([
'principaluri' => 'principals/users/testuser',
Expand Down
2 changes: 1 addition & 1 deletion core/Command/Background/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected function configure(): void {
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$jobId = (int)$input->getArgument('job-id');
$jobId = (string)$input->getArgument('job-id');

$job = $this->jobList->getById($jobId);
if ($job === null) {
Expand Down
4 changes: 2 additions & 2 deletions core/Command/Background/Job.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected function configure(): void {
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$jobId = (int)$input->getArgument('job-id');
$jobId = (string)$input->getArgument('job-id');

$job = $this->jobList->getById($jobId);
if ($job === null) {
Expand Down Expand Up @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 0;
}

protected function printJobInfo(int $jobId, IJob $job, OutputInterface $output): void {
protected function printJobInfo(string $jobId, IJob $job, OutputInterface $output): void {
$row = $this->jobList->getDetailsById($jobId);

$lastRun = new \DateTime();
Expand Down
2 changes: 1 addition & 1 deletion core/Command/Background/JobBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(
parent::__construct();
}

protected function printJobInfo(int $jobId, IJob $job, OutputInterface $output): void {
protected function printJobInfo(string $jobId, IJob $job, OutputInterface $output): void {
$row = $this->jobList->getDetailsById($jobId);

if ($row === null) {
Expand Down
33 changes: 33 additions & 0 deletions core/Migrations/Version33000Date20251124110529.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Migrations;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\Attributes\ModifyColumn;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;

#[ModifyColumn(table: 'jobs', name: 'id', description: 'Remove auto-increment')]
class Version33000Date20251124110529 extends SimpleMigrationStep {
/**
* @param Closure(): ISchemaWrapper $schemaClosure The `\Closure` returns a `ISchemaWrapper`
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
$schema = $schemaClosure();

if ($schema->hasTable('jobs')) {
$schema->dropAutoincrementColumn('jobs', 'id');
}

return $schema;
}
}
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,7 @@
'OC\\Core\\Migrations\\Version33000Date20251023110529' => $baseDir . '/core/Migrations/Version33000Date20251023110529.php',
'OC\\Core\\Migrations\\Version33000Date20251023120529' => $baseDir . '/core/Migrations/Version33000Date20251023120529.php',
'OC\\Core\\Migrations\\Version33000Date20251106131209' => $baseDir . '/core/Migrations/Version33000Date20251106131209.php',
'OC\\Core\\Migrations\\Version33000Date20251124110529' => $baseDir . '/core/Migrations/Version33000Date20251124110529.php',
'OC\\Core\\Migrations\\Version33000Date20251126152410' => $baseDir . '/core/Migrations/Version33000Date20251126152410.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version33000Date20251023110529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251023110529.php',
'OC\\Core\\Migrations\\Version33000Date20251023120529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251023120529.php',
'OC\\Core\\Migrations\\Version33000Date20251106131209' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251106131209.php',
'OC\\Core\\Migrations\\Version33000Date20251124110529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251124110529.php',
'OC\\Core\\Migrations\\Version33000Date20251126152410' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251126152410.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
Expand Down
91 changes: 38 additions & 53 deletions lib/private/BackgroundJob/JobList.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/
namespace OC\BackgroundJob;

use OCP\AppFramework\QueryException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\AutoloadNotAllowedException;
use OCP\BackgroundJob\IJob;
Expand All @@ -17,25 +16,30 @@
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Snowflake\IGenerator;
use Override;
use Psr\Container\ContainerExceptionInterface;
use Psr\Log\LoggerInterface;
use function get_class;
use function json_encode;
use function min;
use function strlen;

class JobList implements IJobList {
/** @var array<string, int> */
/** @var array<string, string> */
protected array $alreadyVisitedParallelBlocked = [];

public function __construct(
protected IDBConnection $connection,
protected IConfig $config,
protected ITimeFactory $timeFactory,
protected LoggerInterface $logger,
protected readonly IDBConnection $connection,
protected readonly IConfig $config,
protected readonly ITimeFactory $timeFactory,
protected readonly LoggerInterface $logger,
protected readonly IGenerator $generator,
) {
}

public function add($job, $argument = null, ?int $firstCheck = null): void {
#[Override]
public function add(IJob|string $job, mixed $argument = null, ?int $firstCheck = null): void {
if ($firstCheck === null) {
$firstCheck = $this->timeFactory->getTime();
}
Expand All @@ -51,6 +55,7 @@ public function add($job, $argument = null, ?int $firstCheck = null): void {
if (!$this->has($job, $argument)) {
$query->insert('jobs')
->values([
'id' => $query->createNamedParameter($this->generator->nextId(), IQueryBuilder::PARAM_INT),
'class' => $query->createNamedParameter($class),
'argument' => $query->createNamedParameter($argumentJson),
'argument_hash' => $query->createNamedParameter(hash('sha256', $argumentJson)),
Expand All @@ -68,15 +73,12 @@ public function add($job, $argument = null, ?int $firstCheck = null): void {
$query->executeStatement();
}

public function scheduleAfter(string $job, int $runAfter, $argument = null): void {
public function scheduleAfter(string $job, int $runAfter, mixed $argument = null): void {
$this->add($job, $argument, $runAfter);
}

/**
* @param IJob|string $job
* @param mixed $argument
*/
public function remove($job, $argument = null): void {
#[Override]
public function remove(IJob|string $job, mixed $argument = null): void {
$class = ($job instanceof IJob) ? get_class($job) : $job;

$query = $this->connection->getQueryBuilder();
Expand Down Expand Up @@ -104,20 +106,16 @@ public function remove($job, $argument = null): void {
}
}

public function removeById(int $id): void {
#[Override]
public function removeById(string $id): void {
$query = $this->connection->getQueryBuilder();
$query->delete('jobs')
->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}

/**
* check if a job is in the list
*
* @param IJob|class-string<IJob> $job
* @param mixed $argument
*/
public function has($job, $argument): bool {
#[Override]
public function has(IJob|string $job, mixed $argument): bool {
$class = ($job instanceof IJob) ? get_class($job) : $job;
$argument = json_encode($argument);

Expand All @@ -135,18 +133,16 @@ public function has($job, $argument): bool {
return (bool)$row;
}

public function getJobs($job, ?int $limit, int $offset): array {
#[Override]
public function getJobs(IJob|string|null $job, ?int $limit, int $offset): array {
$iterable = $this->getJobsIterator($job, $limit, $offset);
return (is_array($iterable))
? $iterable
: iterator_to_array($iterable);
}

/**
* @param IJob|class-string<IJob>|null $job
* @return iterable<IJob> Avoid to store these objects as they may share a Singleton instance. You should instead use these IJobs instances while looping on the iterable.
*/
public function getJobsIterator($job, ?int $limit, int $offset): iterable {
#[Override]
public function getJobsIterator(IJob|string|null $job, ?int $limit, int $offset): iterable {
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('jobs')
Expand All @@ -169,9 +165,7 @@ public function getJobsIterator($job, ?int $limit, int $offset): iterable {
$result->closeCursor();
}

/**
* @inheritDoc
*/
#[Override]
public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = null): ?IJob {
$query = $this->connection->getQueryBuilder();
$query->select('*')
Expand Down Expand Up @@ -279,10 +273,8 @@ public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = nu
}
}

/**
* @return ?IJob The job matching the id. Beware that this object may be a singleton and may be modified by the next call to buildJob.
*/
public function getById(int $id): ?IJob {
#[Override]
public function getById(string $id): ?IJob {
$row = $this->getDetailsById($id);

if ($row) {
Expand All @@ -292,7 +284,8 @@ public function getById(int $id): ?IJob {
return null;
}

public function getDetailsById(int $id): ?array {
#[Override]
public function getDetailsById(string $id): ?array {
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('jobs')
Expand Down Expand Up @@ -320,7 +313,7 @@ private function buildJob(array $row): ?IJob {
// Try to load the job as a service
/** @var IJob $job */
$job = \OCP\Server::get($row['class']);
} catch (QueryException $e) {
} catch (ContainerExceptionInterface $e) {
if (class_exists($row['class'])) {
$class = $row['class'];
$job = new $class();
Expand All @@ -336,7 +329,7 @@ private function buildJob(array $row): ?IJob {
// This most likely means an invalid job was enqueued. We can ignore it.
return null;
}
$job->setId((int)$row['id']);
$job->setId($row['id']);
$job->setLastRun((int)$row['last_run']);
$job->setArgument(json_decode($row['argument'], true));
return $job;
Expand All @@ -351,12 +344,10 @@ private function buildJob(array $row): ?IJob {
*/
public function setLastJob(IJob $job): void {
$this->unlockJob($job);
$this->config->setAppValue('backgroundjob', 'lastjob', (string)$job->getId());
$this->config->setAppValue('backgroundjob', 'lastjob', $job->getId());
}

/**
* Remove the reservation for a job
*/
#[Override]
public function unlockJob(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
Expand All @@ -365,9 +356,7 @@ public function unlockJob(IJob $job): void {
$query->executeStatement();
}

/**
* set the lastRun of $job to now
*/
#[Override]
public function setLastRun(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
Expand All @@ -382,9 +371,7 @@ public function setLastRun(IJob $job): void {
$query->executeStatement();
}

/**
* @param int $timeTaken
*/
#[Override]
public function setExecutionTime(IJob $job, $timeTaken): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
Expand All @@ -394,11 +381,7 @@ public function setExecutionTime(IJob $job, $timeTaken): void {
$query->executeStatement();
}

/**
* Reset the $job so it executes on the next trigger
*
* @since 23.0.0
*/
#[Override]
public function resetBackgroundJob(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
Expand All @@ -408,6 +391,7 @@ public function resetBackgroundJob(IJob $job): void {
$query->executeStatement();
}

#[Override]
public function hasReservedJob(?string $className = null): bool {
$query = $this->connection->getQueryBuilder();
$query->select('*')
Expand All @@ -430,6 +414,7 @@ public function hasReservedJob(?string $className = null): bool {
}
}

#[Override]
public function countByClass(): array {
$query = $this->connection->getQueryBuilder();
$query->select('class')
Expand All @@ -444,7 +429,7 @@ public function countByClass(): array {

while (($row = $result->fetch()) !== false) {
/**
* @var array{count:int, class:class-string} $row
* @var array{count:int, class:class-string<IJob>} $row
*/
$jobs[] = $row;
}
Expand Down
17 changes: 8 additions & 9 deletions lib/public/BackgroundJob/IJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,42 @@ public function start(IJobList $jobList): void;

/**
* @since 7.0.0
* @since 33.0.0 Parameter $id changed from int to string
*/
public function setId(int $id);
public function setId(string $id): void;

/**
* @since 7.0.0
*/
public function setLastRun(int $lastRun);
public function setLastRun(int $lastRun): void;

/**
* @param mixed $argument
* @since 7.0.0
*/
public function setArgument($argument);
public function setArgument(mixed $argument): void;

/**
* Get the id of the background job
* This id is determined by the job list when a job is added to the list
*
* @return int
* @since 7.0.0
* @since 33.0.0 The return type changed from int to string
*/
public function getId();
public function getId(): string;

/**
* Get the last time this job was run as unix timestamp
*
* @return int
* @since 7.0.0
*/
public function getLastRun();
public function getLastRun(): int;

/**
* Get the argument associated with the background job
* This is the argument that will be passed to the background job
*
* @return mixed
* @since 7.0.0
*/
public function getArgument();
public function getArgument(): mixed;
}
Loading
Loading