From b765f79368885ace29d8cd122144a1357435cfbb Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 31 Jan 2022 17:56:43 +0100 Subject: Allow apps to specify if their background job can be delayed Signed-off-by: Joas Schilling --- core/Migrations/Version24000Date20220131153041.php | 55 ++++++++++++++++++++++ lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/BackgroundJob/JobList.php | 17 +++++-- lib/public/BackgroundJob/IJob.php | 9 ++++ lib/public/BackgroundJob/IJobList.php | 5 +- lib/public/BackgroundJob/TimedJob.php | 32 +++++++++++++ tests/lib/BackgroundJob/DummyJobList.php | 3 +- 8 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 core/Migrations/Version24000Date20220131153041.php diff --git a/core/Migrations/Version24000Date20220131153041.php b/core/Migrations/Version24000Date20220131153041.php new file mode 100644 index 00000000000..fd2902c1713 --- /dev/null +++ b/core/Migrations/Version24000Date20220131153041.php @@ -0,0 +1,55 @@ + + * + * @author Joas Schilling + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version24000Date20220131153041 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('jobs'); + if (!$table->hasColumn('time_sensitive')) { + $table->addColumn('time_sensitive', Types::SMALLINT, [ + 'default' => 1, + ]); + $table->addIndex(['time_sensitive'], 'jobs_time_sensitive'); + return $schema; + } + return null; + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 58c6fe9665b..6b0ca70a796 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1001,6 +1001,7 @@ return array( 'OC\\Core\\Migrations\\Version24000Date20211213081604' => $baseDir . '/core/Migrations/Version24000Date20211213081604.php', 'OC\\Core\\Migrations\\Version24000Date20211222112246' => $baseDir . '/core/Migrations/Version24000Date20211222112246.php', 'OC\\Core\\Migrations\\Version24000Date20211230140012' => $baseDir . '/core/Migrations/Version24000Date20211230140012.php', + 'OC\\Core\\Migrations\\Version24000Date20220131153041' => $baseDir . '/core/Migrations/Version24000Date20220131153041.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index d80bead5592..3678f371f2e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1030,6 +1030,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Migrations\\Version24000Date20211213081604' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211213081604.php', 'OC\\Core\\Migrations\\Version24000Date20211222112246' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211222112246.php', 'OC\\Core\\Migrations\\Version24000Date20211230140012' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20211230140012.php', + 'OC\\Core\\Migrations\\Version24000Date20220131153041' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220131153041.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index cece8bdf900..16dfa7b58a6 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -184,9 +184,10 @@ class JobList implements IJobList { /** * get the next job in the list * + * @param bool $onlyTimeSensitive * @return IJob|null */ - public function getNext() { + public function getNext(bool $onlyTimeSensitive = true): ?IJob { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') @@ -195,6 +196,10 @@ class JobList implements IJobList { ->orderBy('last_checked', 'ASC') ->setMaxResults(1); + if ($onlyTimeSensitive) { + $query->andWhere($query->expr()->eq('time_sensitive', $query->createNamedParameter(IJob::TIME_SENSITIVE, IQueryBuilder::PARAM_INT))); + } + $update = $this->connection->getQueryBuilder(); $update->update('jobs') ->set('reserved_at', $update->createNamedParameter($this->timeFactory->getTime())) @@ -215,7 +220,7 @@ class JobList implements IJobList { if ($count === 0) { // Background job already executed elsewhere, try again. - return $this->getNext(); + return $this->getNext($onlyTimeSensitive); } $job = $this->buildJob($row); @@ -229,7 +234,7 @@ class JobList implements IJobList { $reset->execute(); // Background job from disabled app, try again. - return $this->getNext(); + return $this->getNext($onlyTimeSensitive); } return $job; @@ -333,6 +338,12 @@ class JobList implements IJobList { $query->update('jobs') ->set('last_run', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT))); + + if ($job instanceof \OCP\BackgroundJob\TimedJob + && !$job->isTimeSensitive()) { + $query->set('time_sensitive', $query->createNamedParameter(IJob::TIME_INSENSITIVE)); + } + $query->execute(); } diff --git a/lib/public/BackgroundJob/IJob.php b/lib/public/BackgroundJob/IJob.php index 341ae2ac545..3c2da42bf88 100644 --- a/lib/public/BackgroundJob/IJob.php +++ b/lib/public/BackgroundJob/IJob.php @@ -34,6 +34,15 @@ use OCP\ILogger; * @since 7.0.0 */ interface IJob { + /** + * @since 24.0.0 + */ + public const TIME_INSENSITIVE = 0; + /** + * @since 24.0.0 + */ + public const TIME_SENSITIVE = 1; + /** * Run the background job with the registered argument * diff --git a/lib/public/BackgroundJob/IJobList.php b/lib/public/BackgroundJob/IJobList.php index 9f3b485c280..eab37a03f36 100644 --- a/lib/public/BackgroundJob/IJobList.php +++ b/lib/public/BackgroundJob/IJobList.php @@ -85,10 +85,11 @@ interface IJobList { /** * get the next job in the list * + * @param bool $onlyTimeSensitive * @return \OCP\BackgroundJob\IJob|null - * @since 7.0.0 + * @since 7.0.0 - In 24.0.0 parameter $onlyTimeSensitive got added */ - public function getNext(); + public function getNext(bool $onlyTimeSensitive = false): ?IJob; /** * @param int $id diff --git a/lib/public/BackgroundJob/TimedJob.php b/lib/public/BackgroundJob/TimedJob.php index 2cc9ea8e293..579486f6fbf 100644 --- a/lib/public/BackgroundJob/TimedJob.php +++ b/lib/public/BackgroundJob/TimedJob.php @@ -38,6 +38,8 @@ use OCP\ILogger; abstract class TimedJob extends Job { /** @var int */ protected $interval = 0; + /** @var int */ + protected $timeSensitivity = IJob::TIME_SENSITIVE; /** * set the interval for the job @@ -50,6 +52,36 @@ abstract class TimedJob extends Job { $this->interval = $seconds; } + /** + * Whether the background job is time sensitive and needs to run soon after + * the scheduled interval, of if it is okay to be delayed until a later time. + * + * @return bool + * @since 24.0.0 + */ + public function isTimeSensitive(): bool { + return $this->timeSensitivity === IJob::TIME_SENSITIVE; + } + + /** + * If your background job is not time sensitive (sending instant email + * notifications, etc.) it would be nice to set it to IJob::TIME_INSENSITIVE + * This way the execution can be delayed during high usage times. + * + * @param int $sensitivity + * @psalm-param IJob::TIME_* $sensitivity + * @return void + * @since 24.0.0 + */ + public function setTimeSensitivity(int $sensitivity): void { + if ($sensitivity !== IJob::TIME_SENSITIVE && + $sensitivity !== IJob::TIME_INSENSITIVE) { + throw new \InvalidArgumentException('Invalid sensitivity'); + } + + $this->timeSensitivity = $sensitivity; + } + /** * run the job if the last run is is more than the interval ago * diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index 97fc551d8f5..ec06203a477 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -75,9 +75,10 @@ class DummyJobList extends \OC\BackgroundJob\JobList { /** * get the next job in the list * + * @param bool $onlyTimeSensitive * @return IJob|null */ - public function getNext() { + public function getNext(bool $onlyTimeSensitive = true): ?IJob { if (count($this->jobs) > 0) { if ($this->last < (count($this->jobs) - 1)) { $i = $this->last + 1; -- cgit v1.2.3