From 5af7d921a9dcbd03b739d612af203b3d5b9b3605 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 29 Apr 2020 16:07:51 +0200 Subject: [PATCH 1/2] Make Cache::removeChildren non recursive Currently the "add new files during scanning" call stack is smaller than the "remove deleted files during scanning" call stack. This can lead to the scanner adding folders in the folder tree that are to deep to be removed. This changes the `removeChildren` logic to be non recursive so there is no limit to the depth of the folder tree during removal Signed-off-by: Robin Appelman --- lib/private/Files/Cache/Cache.php | 40 ++++++++++++------- lib/private/Files/Cache/CacheQueryBuilder.php | 13 ++++++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index de807421d2..77289e674b 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -553,25 +553,35 @@ class Cache implements ICache { * @throws \OC\DatabaseException */ private function removeChildren(ICacheEntry $entry) { - $children = $this->getFolderContentsById($entry->getId()); - $childIds = array_map(function (ICacheEntry $cacheEntry) { - return $cacheEntry->getId(); - }, $children); - $childFolders = array_filter($children, function ($child) { - return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; - }); - foreach ($childFolders as $folder) { - $this->removeChildren($folder); + $parentIds = [$entry->getId()]; + $queue = [$entry->getId()]; + + // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk + // and collecting all folder ids to later use to delete the filecache entries + while ($entryId = array_pop($queue)) { + $children = $this->getFolderContentsById($entryId); + $childIds = array_map(function (ICacheEntry $cacheEntry) { + return $cacheEntry->getId(); + }, $children); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->execute(); + + /** @var ICacheEntry[] $childFolders */ + $childFolders = array_filter($children, function ($child) { + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; + }); + foreach ($childFolders as $folder) { + $parentIds[] = $folder->getId(); + $queue[] = $folder->getId(); + } } $query = $this->getQueryBuilder(); $query->delete('filecache') - ->whereParent($entry->getId()); - $query->execute(); - - $query = $this->getQueryBuilder(); - $query->delete('filecache_extended') - ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + ->whereParentIn($parentIds); $query->execute(); } diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php index 332274eda2..ac17cfaffb 100644 --- a/lib/private/Files/Cache/CacheQueryBuilder.php +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -94,4 +94,17 @@ class CacheQueryBuilder extends QueryBuilder { return $this; } + + public function whereParentIn(array $parents) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->in("{$alias}parent", $this->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY))); + + return $this; + } } From 8381af626f22585ba154f157eea2bd4431e2f3f1 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Thu, 20 Aug 2020 16:46:00 +0200 Subject: [PATCH 2/2] Accept psalm warning for now Signed-off-by: Morris Jobke --- build/psalm-baseline.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index cf04860f44..8dce521c6f 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -4022,6 +4022,11 @@ $entry + + + $this->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY) + + []