diff --git a/config/config.sample.php b/config/config.sample.php index 20409380e49c4..9e453243fec93 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -2775,4 +2775,12 @@ * Defaults to ``true`` */ 'enable_lazy_objects' => true, + + /** + * Delete previews older than a certain number of days to reduce storage usage. + * Less than one day is not allowed, so set it to 0 to disable the deletion. + * + * Defaults to ``0``. + */ + 'preview_expiration_days' => 0, ]; diff --git a/core/BackgroundJobs/ExpirePreviewsJob.php b/core/BackgroundJobs/ExpirePreviewsJob.php new file mode 100644 index 0000000000000..68e2258a52fc5 --- /dev/null +++ b/core/BackgroundJobs/ExpirePreviewsJob.php @@ -0,0 +1,38 @@ +setTimeSensitivity(IJob::TIME_INSENSITIVE); + $this->setInterval(60 * 60 * 24); + } + + protected function run($argument): void { + $days = $this->config->getSystemValueInt('preview_expiration_days'); + if ($days <= 0) { + return; + } + + $this->service->deleteExpiredPreviews($days); + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index add490d2b0681..323a3d5335d40 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1248,6 +1248,7 @@ 'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => $baseDir . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php', 'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => $baseDir . '/core/BackgroundJobs/CheckForUserCertificates.php', 'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => $baseDir . '/core/BackgroundJobs/CleanupLoginFlowV2.php', + 'OC\\Core\\BackgroundJobs\\ExpirePreviewsJob' => $baseDir . '/core/BackgroundJobs/ExpirePreviewsJob.php', 'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => $baseDir . '/core/BackgroundJobs/GenerateMetadataJob.php', 'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => $baseDir . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php', 'OC\\Core\\BackgroundJobs\\MovePreviewJob' => $baseDir . '/core/BackgroundJobs/MovePreviewJob.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 6c6507f8b22a0..4066ee2ef7a1d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1289,6 +1289,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php', 'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CheckForUserCertificates.php', 'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CleanupLoginFlowV2.php', + 'OC\\Core\\BackgroundJobs\\ExpirePreviewsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/ExpirePreviewsJob.php', 'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/GenerateMetadataJob.php', 'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php', 'OC\\Core\\BackgroundJobs\\MovePreviewJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/MovePreviewJob.php', diff --git a/lib/private/Preview/Db/PreviewMapper.php b/lib/private/Preview/Db/PreviewMapper.php index e6ca2e720f358..ec414cc512c9d 100644 --- a/lib/private/Preview/Db/PreviewMapper.php +++ b/lib/private/Preview/Db/PreviewMapper.php @@ -9,6 +9,8 @@ namespace OC\Preview\Db; +use DateInterval; +use DateTimeImmutable; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; @@ -25,6 +27,7 @@ class PreviewMapper extends QBMapper { private const TABLE_NAME = 'previews'; private const LOCATION_TABLE_NAME = 'preview_locations'; private const VERSION_TABLE_NAME = 'preview_versions'; + public const MAX_CHUNK_SIZE = 1000; public function __construct( IDBConnection $db, @@ -168,19 +171,19 @@ public function getLocationId(string $bucket, string $objectStore): int { } } - public function deleteAll(): void { - $delete = $this->db->getQueryBuilder(); - $delete->delete($this->getTableName()); - } - /** * @return \Generator */ - public function getPreviews(int $lastId, int $limit = 1000): \Generator { + public function getPreviews(int $lastId, int $limit = self::MAX_CHUNK_SIZE, ?int $maxAgeDays = null): \Generator { $qb = $this->db->getQueryBuilder(); $this->joinLocation($qb) ->where($qb->expr()->gt('p.id', $qb->createNamedParameter($lastId, IQueryBuilder::PARAM_INT))) ->setMaxResults($limit); + + if ($maxAgeDays !== null) { + $qb->andWhere($qb->expr()->lt('mtime', $qb->createNamedParameter((new DateTimeImmutable())->sub(new DateInterval('P' . $maxAgeDays . 'D'))->getTimestamp(), IQueryBuilder::PARAM_INT))); + } + return $this->yieldEntities($qb); } diff --git a/lib/private/Preview/PreviewService.php b/lib/private/Preview/PreviewService.php index 8d30ae8a40212..31208d2c46e7b 100644 --- a/lib/private/Preview/PreviewService.php +++ b/lib/private/Preview/PreviewService.php @@ -85,7 +85,7 @@ public function getPreviewsForMimeTypes(array $mimeTypes): \Generator { public function deleteAll(): void { $lastId = 0; while (true) { - $previews = $this->previewMapper->getPreviews($lastId, 1000); + $previews = $this->previewMapper->getPreviews($lastId, PreviewMapper::MAX_CHUNK_SIZE); $i = 0; foreach ($previews as $preview) { $this->deletePreview($preview); @@ -93,7 +93,7 @@ public function deleteAll(): void { $lastId = $preview->getId(); } - if ($i !== 1000) { + if ($i !== PreviewMapper::MAX_CHUNK_SIZE) { break; } } @@ -106,4 +106,21 @@ public function deleteAll(): void { public function getAvailablePreviews(array $fileIds): array { return $this->previewMapper->getAvailablePreviews($fileIds); } + + public function deleteExpiredPreviews(int $maxAgeDays): void { + $lastId = 0; + while (true) { + $previews = $this->previewMapper->getPreviews($lastId, PreviewMapper::MAX_CHUNK_SIZE, $maxAgeDays); + $i = 0; + foreach ($previews as $preview) { + $this->deletePreview($preview); + $i++; + $lastId = $preview->getId(); + } + + if ($i !== PreviewMapper::MAX_CHUNK_SIZE) { + break; + } + } + } } diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 8781127b30afc..3d0c557733048 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -14,6 +14,7 @@ use InvalidArgumentException; use OC\Authentication\Token\PublicKeyTokenProvider; use OC\Authentication\Token\TokenCleanupJob; +use OC\Core\BackgroundJobs\ExpirePreviewsJob; use OC\Core\BackgroundJobs\GenerateMetadataJob; use OC\Core\BackgroundJobs\MovePreviewJob; use OC\Log\Rotate; @@ -507,6 +508,7 @@ public static function installBackgroundJobs(): void { $jobList->add(CleanupDeletedUsers::class); $jobList->add(GenerateMetadataJob::class); $jobList->add(MovePreviewJob::class); + $jobList->add(ExpirePreviewsJob::class); } /**