diff options
author | blizzz <blizzz@arthur-schiwon.de> | 2022-03-10 14:53:53 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-10 14:53:53 +0100 |
commit | da4cd47753b954ed8f6a00a873ab1210bfcf9f24 (patch) | |
tree | 0fe31e46c09ec731b8fae8942cb68e346bc59a1e | |
parent | 06600f60365c84c39a96d84afabc2a9ec98e9312 (diff) | |
parent | 557afa7c1a51f13beb5591ffd86ee7aecae394a4 (diff) | |
download | nextcloud-server-da4cd47753b954ed8f6a00a873ab1210bfcf9f24.tar.gz nextcloud-server-da4cd47753b954ed8f6a00a873ab1210bfcf9f24.zip |
Merge pull request #31122 from nextcloud/backport/30945/stable22
[stable22] Background job time windows
-rw-r--r-- | apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php | 4 | ||||
-rw-r--r-- | apps/dav/lib/BackgroundJob/UploadCleanup.php | 2 | ||||
-rw-r--r-- | apps/files_external/lib/BackgroundJob/CredentialsCleanup.php | 2 | ||||
-rw-r--r-- | apps/files_sharing/lib/ExpireSharesJob.php | 2 | ||||
-rw-r--r-- | apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php | 2 | ||||
-rw-r--r-- | config/config.sample.php | 14 | ||||
-rw-r--r-- | core/Migrations/Version24000Date20220131153041.php | 55 | ||||
-rw-r--r-- | cron.php | 24 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/BackgroundJob/JobList.php | 17 | ||||
-rw-r--r-- | lib/private/Security/Bruteforce/CleanupJob.php | 2 | ||||
-rw-r--r-- | lib/public/BackgroundJob/IJob.php | 9 | ||||
-rw-r--r-- | lib/public/BackgroundJob/IJobList.php | 5 | ||||
-rw-r--r-- | lib/public/BackgroundJob/TimedJob.php | 32 | ||||
-rw-r--r-- | tests/lib/BackgroundJob/DummyJobList.php | 3 |
16 files changed, 167 insertions, 8 deletions
diff --git a/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php b/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php index 2a48452d426..fd370ce60e5 100644 --- a/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php +++ b/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php @@ -27,6 +27,7 @@ namespace OCA\ContactsInteraction\BackgroundJob; use OCA\ContactsInteraction\Db\RecentContactMapper; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\TimedJob; class CleanupJob extends TimedJob { @@ -38,7 +39,8 @@ class CleanupJob extends TimedJob { RecentContactMapper $mapper) { parent::__construct($time); - $this->setInterval(12 * 60 * 60); + $this->setInterval(24 * 60 * 60); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); $this->mapper = $mapper; } diff --git a/apps/dav/lib/BackgroundJob/UploadCleanup.php b/apps/dav/lib/BackgroundJob/UploadCleanup.php index 70f4d6b9565..76906becb54 100644 --- a/apps/dav/lib/BackgroundJob/UploadCleanup.php +++ b/apps/dav/lib/BackgroundJob/UploadCleanup.php @@ -29,6 +29,7 @@ namespace OCA\DAV\BackgroundJob; use OC\User\NoUserException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\IJobList; use OCP\BackgroundJob\TimedJob; use OCP\Files\File; @@ -51,6 +52,7 @@ class UploadCleanup extends TimedJob { // Run once a day $this->setInterval(60 * 60 * 24); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } protected function run($argument) { diff --git a/apps/files_external/lib/BackgroundJob/CredentialsCleanup.php b/apps/files_external/lib/BackgroundJob/CredentialsCleanup.php index 07b66e2ecb6..138d4d7de2d 100644 --- a/apps/files_external/lib/BackgroundJob/CredentialsCleanup.php +++ b/apps/files_external/lib/BackgroundJob/CredentialsCleanup.php @@ -29,6 +29,7 @@ use OCA\Files_External\Lib\Auth\Password\LoginCredentials; use OCA\Files_External\Lib\StorageConfig; use OCA\Files_External\Service\UserGlobalStoragesService; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\TimedJob; use OCP\Security\ICredentialsManager; use OCP\IUser; @@ -53,6 +54,7 @@ class CredentialsCleanup extends TimedJob { // run every day $this->setInterval(24 * 60 * 60); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } protected function run($argument) { diff --git a/apps/files_sharing/lib/ExpireSharesJob.php b/apps/files_sharing/lib/ExpireSharesJob.php index 1e56602a0bf..dd0979e4b0b 100644 --- a/apps/files_sharing/lib/ExpireSharesJob.php +++ b/apps/files_sharing/lib/ExpireSharesJob.php @@ -25,6 +25,7 @@ namespace OCA\Files_Sharing; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\TimedJob; use OCP\IDBConnection; use OCP\Share\Exceptions\ShareNotFound; @@ -50,6 +51,7 @@ class ExpireSharesJob extends TimedJob { // Run once a day $this->setInterval(24 * 60 * 60); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } diff --git a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php index 51428f9b996..3eed1bb8dea 100644 --- a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php +++ b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php @@ -28,6 +28,7 @@ namespace OCA\TwoFactorBackupCodes\BackgroundJob; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\IJobList; use OCP\BackgroundJob\TimedJob; use OCP\IUserManager; @@ -60,6 +61,7 @@ class RememberBackupCodesJob extends TimedJob { $this->jobList = $jobList; $this->setInterval(60 * 60 * 24 * 14); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } protected function run($argument) { diff --git a/config/config.sample.php b/config/config.sample.php index 505728dc47d..4bb976fbcad 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1213,6 +1213,20 @@ $CONFIG = [ */ 'maintenance' => false, +/** + * UTC Hour for maintenance windows + * + * Some background jobs only run once a day. When an hour is defined for this config, + * the background jobs which advertise themselves as not time sensitive will be + * delayed during the "working" hours and only run in the 4 hours after the given time. + * This is e.g. used for activity expiration, suspicious login training and update checks. + * + * A value of 1 e.g. will only run these background jobs between 01:00am UTC and 05:00am UTC. + * + * Defaults to ``100`` which disables the feature + */ +'maintenance_window_start' => 1, + /** * SSL 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 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022 Joas Schilling <coding@nextcloud.com> + * + * @author Joas Schilling <coding@nextcloud.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +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; + } +} @@ -109,6 +109,28 @@ try { $config->setAppValue('core', 'backgroundjobs_mode', 'cron'); } + // Low-load hours + $onlyTimeSensitive = false; + $startHour = $config->getSystemValueInt('maintenance_window_start', 100); + if ($startHour <= 23) { + $date = new \DateTime('now', new \DateTimeZone('UTC')); + $currentHour = (int) $date->format('G'); + $endHour = $startHour + 4; + + if ($startHour <= 20) { + // Start time: 01:00 + // End time: 05:00 + // Only run sensitive tasks when it's before the start or after the end + $onlyTimeSensitive = $currentHour < $startHour || $currentHour > $endHour; + } else { + // Start time: 23:00 + // End time: 03:00 + $endHour -= 24; // Correct the end time from 27:00 to 03:00 + // Only run sensitive tasks when it's after the end and before the start + $onlyTimeSensitive = $currentHour > $endHour && $currentHour < $startHour; + } + } + // Work $jobList = \OC::$server->getJobList(); @@ -118,7 +140,7 @@ try { $endTime = time() + 14 * 60; $executedJobs = []; - while ($job = $jobList->getNext()) { + while ($job = $jobList->getNext($onlyTimeSensitive)) { if (isset($executedJobs[$job->getId()])) { $jobList->unlockJob($job); break; diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 203079853d8..ff7f7737a3d 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -972,6 +972,7 @@ return array( 'OC\\Core\\Migrations\\Version22000Date20210216080825' => $baseDir . '/core/Migrations/Version22000Date20210216080825.php', 'OC\\Core\\Migrations\\Version23000Date20210906132259' => $baseDir . '/core/Migrations/Version23000Date20210906132259.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 7e74dff2f22..4cc4c086674 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1001,6 +1001,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Migrations\\Version22000Date20210216080825' => __DIR__ . '/../../..' . '/core/Migrations/Version22000Date20210216080825.php', 'OC\\Core\\Migrations\\Version23000Date20210906132259' => __DIR__ . '/../../..' . '/core/Migrations/Version23000Date20210906132259.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 8dd9e4466f1..887a3343166 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; @@ -323,6 +328,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/private/Security/Bruteforce/CleanupJob.php b/lib/private/Security/Bruteforce/CleanupJob.php index 1e5f83360d5..6faf853760a 100644 --- a/lib/private/Security/Bruteforce/CleanupJob.php +++ b/lib/private/Security/Bruteforce/CleanupJob.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OC\Security\Bruteforce; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\TimedJob; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -41,6 +42,7 @@ class CleanupJob extends TimedJob { // Run once a day $this->setInterval(3600 * 24); + $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } protected function run($argument) { diff --git a/lib/public/BackgroundJob/IJob.php b/lib/public/BackgroundJob/IJob.php index 341ae2ac545..5e8d4317505 100644 --- a/lib/public/BackgroundJob/IJob.php +++ b/lib/public/BackgroundJob/IJob.php @@ -35,6 +35,15 @@ use OCP\ILogger; */ interface IJob { /** + * @since 22.2.6 + */ + public const TIME_INSENSITIVE = 0; + /** + * @since 22.2.6 + */ + public const TIME_SENSITIVE = 1; + + /** * Run the background job with the registered argument * * @param IJobList $jobList The job list that manages the state of this job diff --git a/lib/public/BackgroundJob/IJobList.php b/lib/public/BackgroundJob/IJobList.php index 299d6725229..2a925999331 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 22.2.6 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..cc2d42dcd32 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 @@ -51,6 +53,36 @@ abstract class TimedJob extends Job { } /** + * 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 22.2.6 + */ + 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 22.2.6 + */ + 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 * * @param JobList $jobList diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index 452b9bb98ed..1c899f35e2a 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; |