Skip to content

[Bug]: PreviewMigrationJob infinite loop when preview files don't exist on disk but entries remain in oc_filecache #59364

@UltimateByte

Description

@UltimateByte

Steps to reproduce

  1. Have a Nextcloud instance with a large number of preview entries in oc_filecache (~462k in our case)
  2. Upgrade to Nextcloud 33.0.0
  3. The preview files on disk may have been cleaned up previously (e.g. via preview:cleanup or manual cleanup), but the corresponding oc_filecache entries remain
  4. Wait for the PreviewMigrationJob background job to trigger

Expected behavior

The PreviewMigrationJob should handle the case where oc_filecache entries exist but the actual preview files are missing on disk. It should either clean up the orphaned entries or skip them and move on.

Actual behavior

The job enters an infinite while(true) loop consuming ~55% CPU for exactly 1 hour (the hardcoded timeout), then restarts on the next cron cycle. Zero previews are ever migrated to the new oc_previews table.

Root cause in core/BackgroundJobs/PreviewMigrationJob.php:

  1. The while(true) loop SELECTs 100 paths from oc_filecache
  2. For each batch, migrateFileId() is called in PreviewMigrationService.php
  3. migrateFileId() calls $this->appData->getFolder($internalPath)
  4. Since the files don't exist on disk, this throws NotFoundException
  5. The catch block returns [] without deleting the orphaned oc_filecache entry
  6. The next iteration SELECTs the same 100 rows → infinite loop

The previewMovedDone flag is never set because the loop never breaks (there are always rows to SELECT).

Proposed fix

In PreviewMigrationService::migrateFileId(), when NotFoundException is caught, the orphaned oc_filecache entries for that preview folder should be cleaned up before returning:

} catch (NotFoundException) {
    // Clean up orphaned filecache entries for this preview folder
    $this->deleteFolder($internalPath);
    return [];
}

This way the while(true) loop in the job will eventually exhaust all entries and set the previewMovedDone flag.

Workaround

-- Remove orphaned preview entries from filecache
DELETE FROM oc_filecache WHERE path LIKE 'appdata_<instanceid>/preview/%';

Then either wait for the job to set the flag itself (it will find 0 rows and complete), or set it manually:

occ config:app:set core previewMovedDone --value=1 --type=boolean

Server configuration

Operating system: Debian 13 (trixie)

Web server: Apache 2.4.66

PHP version: 8.4.19

Nextcloud version: 33.0.0.16

Updated from: 32.x (major upgrade)

Database: MariaDB

Signing status:

No errors have been found.
System configuration
{
    "system": {
        "trusted_domains": [
            "localhost",
            "cloud.lrob.fr"
        ],
        "dbtype": "mysql",
        "version": "33.0.0.16",
        "mysql.utf8mb4": true,
        "memcache.local": "\\OC\\Memcache\\Redis",
        "memcache.distributed": "\\OC\\Memcache\\Redis",
        "filelocking.enabled": true,
        "memcache.locking": "\\OC\\Memcache\\Redis"
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions