99
1010namespace OC \Core \BackgroundJobs ;
1111
12- use OC \Files \Cache \CacheEntry ;
1312use OC \Preview \PreviewMigrationService ;
1413use OCP \AppFramework \Utility \ITimeFactory ;
1514use OCP \BackgroundJob \IJobList ;
1615use OCP \BackgroundJob \TimedJob ;
17- use OCP \DB \QueryBuilder \IQueryBuilder ;
18- use OCP \Files \FileInfo ;
19- use OCP \Files \IMimeTypeLoader ;
2016use OCP \Files \IRootFolder ;
2117use OCP \IAppConfig ;
2218use OCP \IConfig ;
23- use OCP \IDBConnection ;
2419use Override ;
2520use Psr \Log \LoggerInterface ;
2621
@@ -36,8 +31,6 @@ public function __construct(
3631 private readonly IRootFolder $ rootFolder ,
3732 private readonly PreviewMigrationService $ migrationService ,
3833 private readonly IJobList $ jobList ,
39- private readonly IDBConnection $ connection ,
40- private readonly IMimeTypeLoader $ mimeTypeLoader ,
4134 private readonly LoggerInterface $ logger ,
4235 ) {
4336 parent ::__construct ($ time );
@@ -81,8 +74,7 @@ private function runPartition(int $partition): bool {
8174 return true ;
8275 }
8376
84- $ cache = $ storage ->getCache ();
85- $ previewRootId = $ cache ->getId (rtrim ($ this ->previewRootPath , '/ ' ));
77+ $ previewRootId = $ storage ->getCache ()->getId (rtrim ($ this ->previewRootPath , '/ ' ));
8678 if ($ previewRootId === -1 ) {
8779 $ this ->logger ->warning ('Preview migration skipped: no preview root found at "{path}" on storage "{storageId}". ' , [
8880 'path ' => $ this ->previewRootPath ,
@@ -92,97 +84,53 @@ private function runPartition(int $partition): bool {
9284 }
9385
9486 $ startTime = time ();
95- $ storageId = $ cache ->getNumericStorageId ();
96- $ folderMimeTypeId = $ this ->mimeTypeLoader ->getId (FileInfo::MIMETYPE_FOLDER );
97- $ foldersToVisit = [[$ previewRootId , '' , 0 ]];
87+ $ foldersToVisit = [['id ' => $ previewRootId , 'name ' => '' , 'depth ' => 0 ]];
9888 $ foldersToMigrate = [];
9989 // Folders without preview files, by depth; removed once their children are gone.
10090 $ emptyFolders = [];
10191
10292 while ($ foldersToVisit !== []) {
103- $ folders = [];
104- foreach (array_splice ($ foldersToVisit , -self ::BATCH_SIZE ) as [$ folderId , $ folderName , $ depth ]) {
105- if ($ depth === 1 && !$ this ->belongsToPartition ($ folderName , $ partition )) {
106- continue ;
93+ $ folders = array_filter (
94+ array_splice ($ foldersToVisit , -self ::BATCH_SIZE ),
95+ fn (array $ folder ): bool => $ folder ['depth ' ] !== 1 || $ this ->belongsToPartition ($ folder ['name ' ], $ partition ),
96+ );
97+ $ children = $ this ->migrationService ->getFolderChildren (array_column ($ folders , 'id ' ));
98+
99+ foreach ($ folders as $ folder ) {
100+ foreach ($ children [$ folder ['id ' ]]['folders ' ] as $ subFolder ) {
101+ $ foldersToVisit [] = [...$ subFolder , 'depth ' => $ folder ['depth ' ] + 1 ];
107102 }
108- $ folders [$ folderId ] = [$ folderName , $ depth ];
109- }
110- if ($ folders === []) {
111- continue ;
112- }
113103
114- $ entries = array_fill_keys (array_keys ($ folders ), []);
115- $ qb = $ this ->connection ->getQueryBuilder ();
116- $ qb ->select ('fileid ' , 'parent ' , 'name ' , 'mimetype ' , 'size ' , 'mtime ' )
117- ->from ('filecache ' )
118- ->where ($ qb ->expr ()->in ('parent ' , $ qb ->createNamedParameter (array_keys ($ folders ), IQueryBuilder::PARAM_INT_ARRAY )))
119- ->hintShardKey ('storage ' , $ storageId );
120- $ cursor = $ qb ->executeQuery ();
121- while ($ row = $ cursor ->fetchAssociative ()) {
122- $ parent = (int )$ row ['parent ' ];
123- if ((int )$ row ['mimetype ' ] === $ folderMimeTypeId ) {
124- $ foldersToVisit [] = [(int )$ row ['fileid ' ], $ row ['name ' ], $ folders [$ parent ][1 ] + 1 ];
125- } else {
126- $ entries [$ parent ][] = new CacheEntry ($ row );
127- }
128- }
129- $ cursor ->closeCursor ();
130-
131- foreach ($ folders as $ folderId => [$ folderName , $ depth ]) {
132- if ($ entries [$ folderId ] === [] || !ctype_digit ($ folderName )) {
133- if ($ depth > 0 ) {
134- $ emptyFolders [$ depth ][] = $ folderId ;
135- }
136- continue ;
104+ $ files = $ children [$ folder ['id ' ]]['files ' ];
105+ if ($ files !== [] && ctype_digit ($ folder ['name ' ])) {
106+ $ foldersToMigrate [] = [
107+ 'fileId ' => (int )$ folder ['name ' ],
108+ 'folderId ' => $ folder ['id ' ],
109+ 'flat ' => $ folder ['depth ' ] === 1 ,
110+ 'entries ' => $ files ,
111+ ];
112+ } elseif ($ folder ['depth ' ] > 0 ) {
113+ $ emptyFolders [$ folder ['depth ' ]][] = $ folder ['id ' ];
137114 }
138- $ foldersToMigrate [] = ['fileId ' => (int )$ folderName , 'folderId ' => $ folderId , 'flat ' => $ depth === 1 , 'entries ' => $ entries [$ folderId ]];
139115 }
140116
141117 if (count ($ foldersToMigrate ) >= self ::BATCH_SIZE ) {
142- $ this ->migrateFolders ($ foldersToMigrate );
118+ $ this ->migrationService -> migrateFolders ($ foldersToMigrate );
143119 $ foldersToMigrate = [];
144120
145121 if (time () - $ startTime > 3600 ) {
146122 return false ;
147123 }
148124 }
149125 }
150- $ this ->migrateFolders ($ foldersToMigrate );
151-
152- krsort ($ emptyFolders );
153- foreach ($ emptyFolders as $ folderIds ) {
154- foreach (array_chunk ($ folderIds , 1000 ) as $ chunk ) {
155- $ qb = $ this ->connection ->getQueryBuilder ();
156- $ qb ->selectDistinct ('parent ' )
157- ->from ('filecache ' )
158- ->where ($ qb ->expr ()->in ('parent ' , $ qb ->createNamedParameter ($ chunk , IQueryBuilder::PARAM_INT_ARRAY )))
159- ->hintShardKey ('storage ' , $ storageId );
160- $ nonEmpty = array_map ('intval ' , $ qb ->executeQuery ()->fetchFirstColumn ());
161- $ this ->migrationService ->deleteOldFileCacheEntries (array_values (array_diff ($ chunk , $ nonEmpty )));
162- }
163- }
164126
165- return true ;
166- }
127+ $ this -> migrationService -> migrateFolders ( $ foldersToMigrate ) ;
128+ $ this -> migrationService -> deleteEmptyFolders ( $ emptyFolders );
167129
168- /**
169- * @param list<array{fileId: int, folderId: int, flat: bool, entries: list<CacheEntry>}> $folders
170- */
171- private function migrateFolders (array $ folders ): void {
172- try {
173- $ this ->migrationService ->migrateFolders ($ folders );
174- } catch (\Exception $ e ) {
175- $ this ->logger ->error ('Failed to migrate previews of file ids: ' . implode (', ' , array_column ($ folders , 'fileId ' )), [
176- 'exception ' => $ e ,
177- ]);
178- }
130+ return true ;
179131 }
180132
181133 private function belongsToPartition (string $ folderName , int $ partition ): bool {
182- if ($ partition < 0 || $ partition >= self ::PARTITIONS ) {
183- return false ;
184- }
185-
186134 if (ctype_digit ($ folderName )) {
187135 return ((int )$ folderName % self ::PARTITIONS ) === $ partition ;
188136 }
0 commit comments