@ -7,7 +7,6 @@
*/
namespace OC\BackgroundJob;
use OCP\AppFramework\QueryException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\AutoloadNotAllowedException;
use OCP\BackgroundJob\IJob;
@ -17,6 +16,9 @@ use OCP\DB\Exception;
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;
@ -24,18 +26,20 @@ 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();
}
@ -51,6 +55,7 @@ class JobList implements IJobList {
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)),
@ -68,15 +73,12 @@ class JobList implements IJobList {
$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();
@ -104,20 +106,16 @@ class JobList implements IJobList {
}
}
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);
@ -135,18 +133,16 @@ class JobList implements IJobList {
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')
@ -169,9 +165,7 @@ class JobList implements IJobList {
$result->closeCursor();
}
/**
* @inheritDoc
*/
#[Override]
public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = null): ?IJob {
$query = $this->connection->getQueryBuilder();
$query->select('*')
@ -279,10 +273,8 @@ class JobList implements IJobList {
}
}
/**
* @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) {
@ -292,7 +284,8 @@ class JobList implements IJobList {
return null;
}
public function getDetailsById(int $id): ?array {
#[Override]
public function getDetailsById(string $id): ?array {
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('jobs')
@ -320,7 +313,7 @@ class JobList implements IJobList {
// 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();
@ -336,7 +329,7 @@ class JobList implements IJobList {
// 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;
@ -351,12 +344,10 @@ class JobList implements IJobList {
*/
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')
@ -365,9 +356,7 @@ class JobList implements IJobList {
$query->executeStatement();
}
/**
* set the lastRun of $job to now
*/
#[Override]
public function setLastRun(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
@ -382,9 +371,7 @@ class JobList implements IJobList {
$query->executeStatement();
}
/**
* @param int $timeTaken
*/
#[Override]
public function setExecutionTime(IJob $job, $timeTaken): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
@ -394,11 +381,7 @@ class JobList implements IJobList {
$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')
@ -408,6 +391,7 @@ class JobList implements IJobList {
$query->executeStatement();
}
#[Override]
public function hasReservedJob(?string $className = null): bool {
$query = $this->connection->getQueryBuilder();
$query->select('*')
@ -430,6 +414,7 @@ class JobList implements IJobList {
}
}
#[Override]
public function countByClass(): array {
$query = $this->connection->getQueryBuilder();
$query->select('class')
@ -444,7 +429,7 @@ class JobList implements IJobList {
while (($row = $result->fetch()) !== false) {
/**
* @var array{count:int, class:class-string} $row
* @var array{count:int, class:class-string< IJob > } $row
*/
$jobs[] = $row;
}