diff options
author | provokateurin <kate@provokateurin.de> | 2025-04-22 08:59:51 +0200 |
---|---|---|
committer | provokateurin <kate@provokateurin.de> | 2025-04-22 13:09:54 +0200 |
commit | 7bd2de338868c4f1fc64aa3355993a185bc7ad84 (patch) | |
tree | cf193ba3f980ae121b319bb66020fb6f8653f574 | |
parent | fb3f1abec2fd48c18a3153469070784265898d4c (diff) | |
download | nextcloud-server-perf/usermountcache/local-cache.tar.gz nextcloud-server-perf/usermountcache/local-cache.zip |
perf(UserMountCache): Use local cache for user mountsperf/usermountcache/local-cache
Signed-off-by: provokateurin <kate@provokateurin.de>
-rw-r--r-- | lib/private/Files/Config/UserMountCache.php | 73 | ||||
-rw-r--r-- | tests/lib/Files/Config/UserMountCacheTest.php | 3 |
2 files changed, 57 insertions, 19 deletions
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index 50893d2ffcd..17c1a3374fa 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -15,9 +15,12 @@ use OCP\Files\Config\ICachedMountFileInfo; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; use OCP\Files\NotFoundException; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; +use phpDocumentor\Reflection\Types\This; use Psr\Log\LoggerInterface; /** @@ -27,9 +30,8 @@ class UserMountCache implements IUserMountCache { /** * Cached mount info. - * @var CappedMemoryCache<ICachedMountInfo[]> **/ - private CappedMemoryCache $mountsForUsers; + private ICache $mountsForUsers; /** * fileid => internal path mapping for cached mount info. * @var CappedMemoryCache<string> @@ -46,10 +48,11 @@ class UserMountCache implements IUserMountCache { private IUserManager $userManager, private LoggerInterface $logger, private IEventLogger $eventLogger, + private ICacheFactory $cacheFactory, ) { $this->cacheInfoCache = new CappedMemoryCache(); $this->internalPathCache = new CappedMemoryCache(); - $this->mountsForUsers = new CappedMemoryCache(); + $this->mountsForUsers = $this->cacheFactory->createLocal('user-mounts'); } public function registerMounts(IUser $user, array $mounts, ?array $mountProviderClasses = null) { @@ -96,29 +99,30 @@ class UserMountCache implements IUserMountCache { if ($addedMounts || $removedMounts || $changedMounts) { $this->connection->beginTransaction(); $userUID = $user->getUID(); + try { foreach ($addedMounts as $mount) { $this->logger->debug("Adding mount '{$mount->getKey()}' for user '$userUID'", ['app' => 'files', 'mount_provider' => $mount->getMountProvider()]); $this->addToCache($mount); - /** @psalm-suppress InvalidArgument */ - $this->mountsForUsers[$userUID][$mount->getKey()] = $mount; + $cachedMounts[$mount->getKey()] = $mount; } foreach ($removedMounts as $mount) { $this->logger->debug("Removing mount '{$mount->getKey()}' for user '$userUID'", ['app' => 'files', 'mount_provider' => $mount->getMountProvider()]); $this->removeFromCache($mount); - unset($this->mountsForUsers[$userUID][$mount->getKey()]); + unset($cachedMounts[$mount->getKey()]); } foreach ($changedMounts as $mount) { $this->logger->debug("Updating mount '{$mount->getKey()}' for user '$userUID'", ['app' => 'files', 'mount_provider' => $mount->getMountProvider()]); $this->updateCachedMount($mount); - /** @psalm-suppress InvalidArgument */ - $this->mountsForUsers[$userUID][$mount->getKey()] = $mount; + $cachedMounts[$mount->getKey()] = $mount; } $this->connection->commit(); } catch (\Throwable $e) { $this->connection->rollBack(); throw $e; } + + $this->mountsForUsers->set($userUID, array_map(fn (ICachedMountInfo $cachedMountInfo) => $this->mountInfoToDbRow($cachedMountInfo), $cachedMounts), 60); } $this->eventLogger->end('fs:setup:user:register'); } @@ -220,15 +224,50 @@ class UserMountCache implements IUserMountCache { } /** + * Converts the cached mount info into an array to be parsed by @see UserMountCache::dbRowToMountInfo + * + * The 'path' is intentionally omitted to keep the laziness, so any attempt at calling @see UserMountCache::dbRowToMountInfo must pass the $pathCallback parameter. + */ + private function mountInfoToDbRow(ICachedMountInfo $cachedMountInfo): array { + return [ + 'user_id' => $cachedMountInfo->getUser()->getUID(), + 'mount_id' => $cachedMountInfo->getMountId(), + 'storage_id' => $cachedMountInfo->getStorageId(), + 'root_id' => $cachedMountInfo->getRootId(), + 'mount_point' => $cachedMountInfo->getMountPoint(), + 'mount_provider_class' => $cachedMountInfo->getMountProvider(), + ]; + } + + /** * @param IUser $user - * @return ICachedMountInfo[] + * @return array<string, ICachedMountInfo> */ public function getMountsForUser(IUser $user) { $userUID = $user->getUID(); if (!$this->userManager->userExists($userUID)) { return []; } - if (!isset($this->mountsForUsers[$userUID])) { + + $rawMounts = $this->mountsForUsers->get($userUID); + $mounts = null; + if ($rawMounts !== null) { + $mounts = []; + foreach ($rawMounts as $serializedMount) { + try { + $mount = $this->dbRowToMountInfo($serializedMount, [$this, 'getInternalPathForMountInfo']); + $mounts[$mount->getKey()] = $mount; + } catch (\Exception) { + // Discard all cached mounts if we fail to parse any of the cached mounts. + $mounts = null; + break; + } + } + } + + if ($mounts === null) { + $mounts = []; + $builder = $this->connection->getQueryBuilder(); $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'mount_provider_class') ->from('mounts', 'm') @@ -238,17 +277,15 @@ class UserMountCache implements IUserMountCache { $rows = $result->fetchAll(); $result->closeCursor(); - /** @var array<string, ICachedMountInfo> $mounts */ - $mounts = []; foreach ($rows as $row) { $mount = $this->dbRowToMountInfo($row, [$this, 'getInternalPathForMountInfo']); - if ($mount !== null) { - $mounts[$mount->getKey()] = $mount; - } + $mounts[$mount->getKey()] = $mount; } - $this->mountsForUsers[$userUID] = $mounts; + + $this->mountsForUsers->set($userUID, array_map(fn (ICachedMountInfo $cachedMountInfo) => $this->mountInfoToDbRow($cachedMountInfo), $mounts), 60); } - return $this->mountsForUsers[$userUID]; + + return $mounts; } public function getInternalPathForMountInfo(CachedMountInfo $info): string { @@ -447,7 +484,7 @@ class UserMountCache implements IUserMountCache { public function clear(): void { $this->cacheInfoCache = new CappedMemoryCache(); - $this->mountsForUsers = new CappedMemoryCache(); + $this->mountsForUsers = $this->cacheFactory->createLocal('user-mounts'); } public function getMountForPath(IUser $user, string $path): ICachedMountInfo { diff --git a/tests/lib/Files/Config/UserMountCacheTest.php b/tests/lib/Files/Config/UserMountCacheTest.php index 212dd97f9a3..f67db01f16c 100644 --- a/tests/lib/Files/Config/UserMountCacheTest.php +++ b/tests/lib/Files/Config/UserMountCacheTest.php @@ -20,6 +20,7 @@ use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; +use OCP\Server; use Psr\Log\LoggerInterface; use Test\TestCase; use Test\Util\User\Dummy; @@ -67,7 +68,7 @@ class UserMountCacheTest extends TestCase { $userBackend->createUser('u2', ''); $userBackend->createUser('u3', ''); $this->userManager->registerBackend($userBackend); - $this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class), $this->createMock(IEventLogger::class)); + $this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class), $this->createMock(IEventLogger::class), Server::get(ICacheFactory::class)); } protected function tearDown(): void { |