Skip to content

Conversation

@provokateurin
Copy link
Member

Closes #45962

This is disabled by default, to avoid potentially heavy load for servers which need to regenerate previews. Admins should only enable this is if storage usage is a concern and they know which expiration threshold is good for their use-case.

@provokateurin provokateurin added this to the Nextcloud 33 milestone Oct 8, 2025
@provokateurin provokateurin requested a review from a team as a code owner October 8, 2025 16:25
@provokateurin provokateurin requested review from come-nc and salmart-dev and removed request for a team October 8, 2025 16:25
@provokateurin provokateurin added enhancement 3. to review Waiting for reviews labels Oct 8, 2025
@provokateurin provokateurin changed the title chore(PreviewMapper): Remove unused and broken deleteAll method feat(preview): Expire previews Oct 8, 2025
@provokateurin provokateurin force-pushed the feat/preview/expire-previews branch from 53c1edc to fce9e12 Compare October 8, 2025 16:31
@tcitworld
Copy link
Member

How are the actual preview files deleted?

@provokateurin
Copy link
Member Author

Right, I forgot something, unless this is somehow done already 😂

@provokateurin provokateurin marked this pull request as draft October 8, 2025 16:35
@provokateurin provokateurin added 2. developing Work in progress and removed 3. to review Waiting for reviews labels Oct 8, 2025
Signed-off-by: provokateurin <kate@provokateurin.de>
Signed-off-by: provokateurin <kate@provokateurin.de>
@provokateurin provokateurin force-pushed the feat/preview/expire-previews branch from fce9e12 to fee65e5 Compare October 9, 2025 07:53
@provokateurin provokateurin added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Oct 9, 2025
@provokateurin provokateurin marked this pull request as ready for review October 9, 2025 07:53
@provokateurin
Copy link
Member Author

Now :)


public function deleteExpiredPreviews(int $maxAgeDays): void {
$lastId = 0;
while (true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do everything at once? If an admin suddenly sets preview_expiration_days to a very low value, there will need a lot of work to delete everything, so the cron might timeout at some point.

The fact that the background job runs regularly should be enough to lower the number of previews little by little. And we can also add a command to cleanup everything that won't be limited if needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but if it is limited to some value per run, then the job might not be able to catch up with deleting all expired previews.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sensitive defaults should be acceptable. Maybe increase the job frequency to make sure this works?

Copy link
Member

@CarlSchwan CarlSchwan Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A common pattern we have elsewhere is to limit by time. e.g. max 1 hour running

$startTime = time();
		while (true) {
			$done = $this->doTheStuffWithLimit(1000);

                        if ($done) {
                            break;
                        }

			// Stop if execution time is more than one hour.
			if (time() - $startTime > 3600) {
				return;
			}

while (true) {
$previews = $this->previewMapper->getPreviews($lastId, PreviewMapper::MAX_CHUNK_SIZE, $maxAgeDays);
$i = 0;
foreach ($previews as $preview) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense to wrap this in a transation and delete by chunk to speed up a bit the process

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I just copied from the deleteAll method which doesn't have that (probably because it will only be used by tests?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's currently only used in tests

Copy link
Member Author

@provokateurin provokateurin Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm but with a transaction the DB and storage could get out of sync and I don't know how that is handled. What happens if the files are deleted, but the entries are still in the DB?

(Ok they can get out of sync without a transaction as well, but it would be limited to a single preview and not all expired ones)

Copy link
Member

@CarlSchwan CarlSchwan Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't call rollback but just commit if something fails in the middle while removing a preview from the storage?

This was referenced Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add expire mechanism for preview images

4 participants