@ -12,11 +12,10 @@ namespace OC\Core\BackgroundJobs;
use OC\Preview\PreviewMigrationService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\DB\IResult ;
use OCP\Files\FileInfo ;
use OCP\Files\IRootFolder;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use Override;
use Psr\Log\LoggerInterface;
@ -27,7 +26,6 @@ class PreviewMigrationJob extends TimedJob {
ITimeFactory $time,
private readonly IAppConfig $appConfig,
private readonly IConfig $config,
private readonly IDBConnection $connection,
private readonly IRootFolder $rootFolder,
private readonly PreviewMigrationService $migrationService,
private readonly LoggerInterface $logger,
@ -45,79 +43,60 @@ class PreviewMigrationJob extends TimedJob {
return;
}
$startTime = time();
while (true) {
$qb = $this->connection->getQueryBuilder();
$qb->select('path')
->from('filecache')
->where($qb->expr()->orX(
// Hierarchical preview folder structure
$qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')),
// Legacy flat preview folder structure
$qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%'))
))->andWhere(
$qb->expr()->eq('storage', $qb->createNamedParameter($this->rootFolder->getMountPoint()->getNumericStorageId()))
)
->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
->setMaxResults(100);
$result = $qb->executeQuery();
$foundPreviews = $this->processQueryResult($result);
if (!$foundPreviews) {
break;
}
$storage = $this->rootFolder->getMountPoint()->getStorage();
if ($storage === null) {
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
return;
}
// Stop if execution time is more than one hour.
if (time() - $startTime > 3600) {
return;
}
$cache = $storage->getCache();
$previewRootId = $cache->getId(rtrim($this->previewRootPath, '/'));
if ($previewRootId === -1) {
// No previews have ever been generated on this instance.
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
return;
}
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
}
$startTime = time();
private function processQueryResult(IResult $result): bool {
$foundPreview = false;
$fileIds = [];
$flatFileIds = [];
while ($row = $result->fetch()) {
$pathSplit = explode('/', $row['path']);
assert(count($pathSplit) >= 2) ;
$fileId = (int)$pathSplit[count($pathSplit) - 2];
if (count($pathSplit) === 11 ) {
// Hierarchical structure
if (!in_array($fileId, $fileIds)) {
$fileIds[] = $fileId ;
}
} else {
// Flat structure
if (!in_array($fileId, $flatFileIds)) {
$flatFileIds[] = $fileId ;
// Walk the preview folder tree via the `parent` column, which is indexed on
// every supported database platform.
//
// Depth from the preview root tells us which structure a leaf folder holds:
// - depth 1: legacy flat structure, e.g. preview/< fileid > /< size > .png
// - depth 8: hierarchical structure, e.g. preview/a/b/c/d/e/f/g/< fileid > /< size > .png
$foldersToVisit = [[$previewRootId, '', 0]] ;
while ($foldersToVisit !== [] ) {
[$folderId, $folderName, $depth] = array_pop($foldersToVisit);
$hasPreviewFiles = false ;
foreach ($cache->getFolderContentsById($folderId) as $entry) {
if ($entry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
$foldersToVisit[] = [$entry->getId(), $entry->getName(), $depth + 1];
} else {
$hasPreviewFiles = true ;
}
}
$foundPreview = true;
}
foreach ($fileIds as $fileId) {
try {
$this->migrationService->migrateFileId($fileId, flatPath: false);
} catch (\Exception $e) {
$this->logger->error('Failed to migrate preview with fileId: ' . $fileId . ' (hierarchical file structure)', [
'exception' => $e,
]);
if (!$hasPreviewFiles || !ctype_digit($folderName)) {
continue;
}
}
foreach ($flatFileIds as $fileId) {
try {
$this->migrationService->migrateFileId($fileId, flatPath: true );
$this->migrationService->migrateFileId((int)$folderName, flatPath: $depth === 1);
} catch (\Exception $e) {
$this->logger->error('Failed to migrate preview with fileId: ' . $fileId . ' (legacy file structure)' , [
$this->logger->error('Failed to migrate preview with fileId: ' . $folderName , [
'exception' => $e,
]);
}
// Stop if execution time is more than one hour.
if (time() - $startTime > 3600) {
return;
}
}
return $foundPreview;
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
}
}