Skip to content

Commit 0100343

Browse files
Merge pull request #4338 from nextcloud/backport/4320/stable33
[stable33] Performance optimisation for high number of team folders
2 parents c2b8bbc + f04c6d7 commit 0100343

File tree

2 files changed

+45
-25
lines changed

2 files changed

+45
-25
lines changed

lib/Controller/FolderController.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,8 @@ private function checkMountPointExists(string $mountpoint): ?DataResponse {
207207
throw new OCSNotFoundException('Groupfolder not found');
208208
}
209209

210-
$folders = $this->manager->getAllFolders();
211-
foreach ($folders as $folder) {
212-
if ($folder->mountPoint === $mountpoint) {
213-
throw new OCSBadRequestException('Mount point already exists');
214-
}
210+
if ($this->manager->mountPointExists($mountpoint)) {
211+
throw new OCSBadRequestException('Mount point already exists');
215212
}
216213

217214
return null;

lib/Folder/FolderManager.php

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,6 @@ private function selectWithFileCache(?IQueryBuilder $query = null): IQueryBuilde
137137
* @throws Exception
138138
*/
139139
public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, string $orderBy = 'mount_point', string $order = 'ASC'): array {
140-
$applicableMap = $this->getAllApplicable();
141-
142140
$query = $this->selectWithFileCache();
143141
$query->setFirstResult($offset);
144142
$query->setMaxResults($limit);
@@ -157,7 +155,9 @@ public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, strin
157155

158156
$rows = $query->executeQuery()->fetchAll();
159157

160-
$folderMappings = $this->getAllFolderMappings();
158+
$folderIds = array_values(array_map(static fn (array $row): int => (int)$row['folder_id'], $rows));
159+
$applicableMap = $this->getAllApplicable($folderIds);
160+
$folderMappings = $this->getAllFolderMappings($folderIds);
161161

162162
$folderMap = [];
163163
foreach ($rows as $row) {
@@ -182,7 +182,6 @@ public function getAllFoldersWithSize(int $offset = 0, ?int $limit = null, strin
182182
*/
183183
public function getAllFoldersForUserWithSize(IUser $user): array {
184184
$groups = $this->groupManager->getUserGroupIds($user);
185-
$applicableMap = $this->getAllApplicable();
186185

187186
$query = $this->selectWithFileCache();
188187
$query->innerJoin(
@@ -196,7 +195,9 @@ public function getAllFoldersForUserWithSize(IUser $user): array {
196195

197196
$rows = $query->executeQuery()->fetchAll();
198197

199-
$folderMappings = $this->getAllFolderMappings();
198+
$folderIds = array_values(array_map(static fn (array $row): int => (int)$row['folder_id'], $rows));
199+
$applicableMap = $this->getAllApplicable($folderIds);
200+
$folderMappings = $this->getAllFolderMappings($folderIds);
200201

201202
$folderMap = [];
202203
foreach ($rows as $row) {
@@ -219,23 +220,27 @@ public function getAllFoldersForUserWithSize(IUser $user): array {
219220
* @return array<int, list<InternalFolderMapping>>
220221
* @throws Exception
221222
*/
222-
private function getAllFolderMappings(): array {
223+
private function getAllFolderMappings(?array $folderIds = null): array {
224+
if ($folderIds === []) {
225+
return [];
226+
}
227+
223228
$query = $this->connection->getQueryBuilder();
224229

225230
$query->select('*')
226231
->from('group_folders_manage', 'g');
232+
if ($folderIds !== null) {
233+
$query->where($query->expr()->in('folder_id', $query->createNamedParameter($folderIds, IQueryBuilder::PARAM_INT_ARRAY)));
234+
}
227235

228236
$rows = $query->executeQuery()->fetchAll();
229237

230238
$folderMap = [];
231239
foreach ($rows as $row) {
232240
$id = (int)$row['folder_id'];
233241

234-
if (!isset($folderMap[$id])) {
235-
$folderMap[$id] = [$row];
236-
} else {
237-
$folderMap[$id][] = $row;
238-
}
242+
$folderMap[$id] ??= [];
243+
$folderMap[$id][] = $row;
239244
}
240245

241246
return $folderMap;
@@ -301,8 +306,6 @@ private function getManageAcl(array $mappings): array {
301306
}
302307

303308
public function getFolder(int $id): ?FolderWithMappingsAndCache {
304-
$applicableMap = $this->getAllApplicable();
305-
306309
$query = $this->selectWithFileCache();
307310

308311
$query->where($query->expr()->eq('f.folder_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
@@ -314,14 +317,15 @@ public function getFolder(int $id): ?FolderWithMappingsAndCache {
314317
return null;
315318
}
316319

320+
$applicableMap = $this->getAllApplicable([$id])[$id] ?? [];
317321
$folderMappings = $this->getFolderMappings($id);
318322

319323
$folder = $this->rowToFolder($row);
320324
$id = $folder->id;
321325
return FolderWithMappingsAndCache::fromFolderWithMapping(
322326
FolderDefinitionWithMappings::fromFolder(
323327
$folder,
324-
$applicableMap[$id] ?? [],
328+
$applicableMap,
325329
$this->getManageAcl($folderMappings),
326330
),
327331
Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
@@ -358,12 +362,19 @@ public function getFolderByPath(string $path): int {
358362
* @return array<int, array<string, GroupFoldersApplicable>>
359363
* @throws Exception
360364
*/
361-
private function getAllApplicable(): array {
365+
private function getAllApplicable(?array $folderIds = null): array {
366+
if ($folderIds === []) {
367+
return [];
368+
}
369+
362370
$queryHelper = $this->getCirclesManager()?->getQueryHelper();
363371

364372
$query = $queryHelper?->getQueryBuilder() ?? $this->connection->getQueryBuilder();
365373
$query->select('g.folder_id', 'g.group_id', 'g.circle_id', 'g.permissions')
366374
->from('group_folders_groups', 'g');
375+
if ($folderIds !== null) {
376+
$query->where($query->expr()->in('g.folder_id', $query->createNamedParameter($folderIds, IQueryBuilder::PARAM_INT_ARRAY)));
377+
}
367378

368379
$queryHelper?->addCircleDetails('g', 'circle_id');
369380

@@ -374,9 +385,7 @@ private function getAllApplicable(): array {
374385

375386
foreach ($rows as $row) {
376387
$id = (int)$row['folder_id'];
377-
if (!array_key_exists($id, $applicableMap)) {
378-
$applicableMap[$id] = [];
379-
}
388+
$applicableMap[$id] ??= [];
380389

381390
if (!$row['circle_id']) {
382391
$entityId = (string)$row['group_id'];
@@ -414,7 +423,7 @@ private function getAllApplicable(): array {
414423
* @throws Exception
415424
*/
416425
private function getGroups(int $id): array {
417-
$groups = $this->getAllApplicable()[$id] ?? [];
426+
$groups = $this->getAllApplicable([$id])[$id] ?? [];
418427
$groups = array_map($this->groupManager->get(...), array_keys($groups));
419428

420429
return array_map(fn (IGroup $group): array => [
@@ -428,7 +437,7 @@ private function getGroups(int $id): array {
428437
* @throws Exception
429438
*/
430439
private function getCircles(int $id): array {
431-
$circles = $this->getAllApplicable()[$id] ?? [];
440+
$circles = $this->getAllApplicable([$id])[$id] ?? [];
432441
$circles = array_map($this->getCircle(...), array_keys($circles));
433442

434443
// get nested teams
@@ -479,6 +488,20 @@ public function canManageACL(int $folderId, IUser $user): bool {
479488
return $this->userMappingManager->userInMappings($user, $managerMappings);
480489
}
481490

491+
public function mountPointExists(string $mountPoint): bool {
492+
$query = $this->connection->getQueryBuilder();
493+
$query->select($query->func()->count('*'))
494+
->from('group_folders')
495+
->where($query->expr()->eq('mount_point', $query->createNamedParameter($mountPoint)))
496+
->setMaxResults(1);
497+
498+
$result = $query->executeQuery();
499+
$exists = (int)$result->fetchOne() > 0;
500+
$result->closeCursor();
501+
502+
return $exists;
503+
}
504+
482505
/**
483506
* @return IUserMapping[]
484507
*/

0 commit comments

Comments
 (0)