]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat(BackgroundJobs): Allow preventing parallel runs for a job class
authorMarcel Klehr <mklehr@gmx.net>
Thu, 20 Apr 2023 10:55:06 +0000 (12:55 +0200)
committerMarcel Klehr <mklehr@gmx.net>
Sun, 23 Apr 2023 10:36:12 +0000 (12:36 +0200)
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
lib/private/BackgroundJob/JobList.php
lib/public/BackgroundJob/IJobList.php
lib/public/BackgroundJob/Job.php

index 67b736b8dd96c978e3db1c0f52d8ad33b70c32b0..6761aa282d155fce4b093b3294e468507db3c2d1 100644 (file)
@@ -35,6 +35,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
 use OCP\AutoloadNotAllowedException;
 use OCP\BackgroundJob\IJob;
 use OCP\BackgroundJob\IJobList;
+use OCP\DB\Exception;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 use OCP\IConfig;
 use OCP\IDBConnection;
@@ -382,4 +383,21 @@ class JobList implements IJobList {
                        ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId()), IQueryBuilder::PARAM_INT));
                $query->executeStatement();
        }
+
+       public function hasReservedJob(?string $className): bool {
+               $query = $this->connection->getQueryBuilder();
+               $query->select('*')
+                       ->from('jobs')
+                       ->where($query->expr()->neq('reserved_at', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
+
+               if ($className !== null) {
+                       $query->andWhere($query->expr()->eq('class', $query->createNamedParameter($className)));
+               }
+
+               try {
+                       return $query->executeQuery()->rowCount() > 0;
+               } catch (Exception $e) {
+                       return false;
+               }
+       }
 }
index e8d0380e604e62eba7921435aef855aeaf2c8861..71faefb882562ce1276f2b852a59c451fc8b0459 100644 (file)
@@ -145,4 +145,13 @@ interface IJobList {
         * @since 23.0.0
         */
        public function resetBackgroundJob(IJob $job): void;
+
+       /**
+        * Checks whether a job of the passed class is reserved to run
+        *
+        * @param string|null $className
+        * @return bool
+        * @since 27.0.0
+        */
+       public function hasReservedJob(?string $className): bool;
 }
index d60fb5905c9d187142080129a0f1795eaf9edb6a..89ea41381c96f5819a8b0ed627c59a9a74c4715f 100644 (file)
@@ -43,6 +43,7 @@ abstract class Job implements IJob {
        protected int $lastRun = 0;
        protected $argument;
        protected ITimeFactory $time;
+       protected bool $allowParallelRuns = true;
 
        /**
         * @since 15.0.0
@@ -72,6 +73,11 @@ abstract class Job implements IJob {
                $jobList->setLastRun($this);
                $logger = \OCP\Server::get(LoggerInterface::class);
 
+               if (!$this->getAllowParallelRuns() && $jobList->hasReservedJob(get_class($this))) {
+                       $logger->debug('Skipping ' . get_class($this) . ' job with ID ' . $this->getId() . ' because another job with the same class is already running', ['app' => 'cron']);
+                       return;
+               }
+
                try {
                        $jobStartTime = $this->time->getTime();
                        $logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']);
@@ -132,6 +138,25 @@ abstract class Job implements IJob {
                return $this->argument;
        }
 
+       /**
+        * Set this to false to prevent two Jobs from this class from running in parallel
+        *
+        * @param bool $allow
+        * @return void
+        * @since 27.0.0
+        */
+       public function setAllowParallelRuns(bool $allow) {
+               $this->allowParallelRuns = $allow;
+       }
+
+       /**
+        * @return bool
+        * @since 27.0.0
+        */
+       public function getAllowParallelRuns(): bool {
+               return $this->allowParallelRuns;
+       }
+
        /**
         * The actual function that is called to run the job
         *