diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index b5b07d8f29e51..172d85d43aaee 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -26,8 +26,10 @@
* along with this program. If not, see
*
*/
+
namespace OC\Files\Config;
+use OC\Files\Cache\FileAccess;
use OC\User\LazyUser;
use OCP\Cache\CappedMemoryCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
@@ -45,37 +47,31 @@
* Cache mounts points per user in the cache so we can easily look them up
*/
class UserMountCache implements IUserMountCache {
- private IDBConnection $connection;
- private IUserManager $userManager;
-
/**
* Cached mount info.
+ *
* @var CappedMemoryCache
**/
private CappedMemoryCache $mountsForUsers;
/**
* fileid => internal path mapping for cached mount info.
+ *
* @var CappedMemoryCache
**/
private CappedMemoryCache $internalPathCache;
- private LoggerInterface $logger;
/** @var CappedMemoryCache */
private CappedMemoryCache $cacheInfoCache;
- private IEventLogger $eventLogger;
/**
* UserMountCache constructor.
*/
public function __construct(
- IDBConnection $connection,
- IUserManager $userManager,
- LoggerInterface $logger,
- IEventLogger $eventLogger
+ private IDBConnection $connection,
+ private IUserManager $userManager,
+ private LoggerInterface $logger,
+ private IEventLogger $eventLogger,
+ private FileAccess $cacheAccess,
) {
- $this->connection = $connection;
- $this->userManager = $userManager;
- $this->logger = $logger;
- $this->eventLogger = $eventLogger;
$this->cacheInfoCache = new CappedMemoryCache();
$this->internalPathCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
@@ -282,11 +278,8 @@ public function getInternalPathForMountInfo(CachedMountInfo $info): string {
if ($cached !== null) {
return $cached;
}
- $builder = $this->connection->getQueryBuilder();
- $query = $builder->select('path')
- ->from('filecache')
- ->where($builder->expr()->eq('fileid', $builder->createPositionalParameter($info->getRootId())));
- return $query->executeQuery()->fetchOne() ?: '';
+ $entry = $this->cacheAccess->getByFileIdInStorage($info->getRootId(), $info->getStorageId());
+ return $entry ? $entry->getPath() : '';
}
/**
@@ -294,11 +287,10 @@ public function getInternalPathForMountInfo(CachedMountInfo $info): string {
* @param string|null $user limit the results to a single user
* @return CachedMountInfo[]
*/
- public function getMountsForStorageId($numericStorageId, $user = null) {
+ public function getMountsForStorageId($numericStorageId, $user = null, bool $preloadPaths = false) {
$builder = $this->connection->getQueryBuilder();
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
+ $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'mount_provider_class')
->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
if ($user) {
@@ -309,7 +301,21 @@ public function getMountsForStorageId($numericStorageId, $user = null) {
$rows = $result->fetchAll();
$result->closeCursor();
- return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
+ if ($preloadPaths) {
+ $fileIds = array_map(fn (array $row) => $row['root_id'], $rows);
+ $files = $this->cacheAccess->getByFileIds($fileIds);
+
+ foreach ($rows as &$row) {
+ $mountFileId = $row['root_id'];
+ if (isset($files[$mountFileId])) {
+ $row['path'] = $files[$mountFileId]->getPath();
+ }
+ }
+ }
+
+ return array_filter(array_map(function (array $row) use ($preloadPaths) {
+ return $this->dbRowToMountInfo($row, $preloadPaths ? null : [$this, 'getInternalPathForMountInfo']);
+ }, $rows));
}
/**
@@ -318,45 +324,17 @@ public function getMountsForStorageId($numericStorageId, $user = null) {
*/
public function getMountsForRootId($rootFileId) {
$builder = $this->connection->getQueryBuilder();
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
+ $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'mount_provider_class')
->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
$result = $query->execute();
$rows = $result->fetchAll();
$result->closeCursor();
- return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
- }
-
- /**
- * @param $fileId
- * @return array{int, string, int}
- * @throws \OCP\Files\NotFoundException
- */
- private function getCacheInfoFromFileId($fileId): array {
- if (!isset($this->cacheInfoCache[$fileId])) {
- $builder = $this->connection->getQueryBuilder();
- $query = $builder->select('storage', 'path', 'mimetype')
- ->from('filecache')
- ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
-
- $result = $query->execute();
- $row = $result->fetch();
- $result->closeCursor();
-
- if (is_array($row)) {
- $this->cacheInfoCache[$fileId] = [
- (int)$row['storage'],
- (string)$row['path'],
- (int)$row['mimetype']
- ];
- } else {
- throw new NotFoundException('File with id "' . $fileId . '" not found');
- }
- }
- return $this->cacheInfoCache[$fileId];
+ return array_filter(array_map(function (array $row) {
+ return $this->dbRowToMountInfo($row, [$this, 'getInternalPathForMountInfo']);
+ }, $rows));
}
/**
@@ -366,12 +344,13 @@ private function getCacheInfoFromFileId($fileId): array {
* @since 9.0.0
*/
public function getMountsForFileId($fileId, $user = null) {
- try {
- [$storageId, $internalPath] = $this->getCacheInfoFromFileId($fileId);
- } catch (NotFoundException $e) {
+ $cacheEntry = $this->cacheAccess->getByFileId($fileId);
+ if (!$cacheEntry) {
return [];
}
- $mountsForStorage = $this->getMountsForStorageId($storageId, $user);
+ $internalPath = $cacheEntry->getPath();
+
+ $mountsForStorage = $this->getMountsForStorageId($cacheEntry->getStorageId(), $user, true);
// filter mounts that are from the same storage but not a parent of the file we care about
$filteredMounts = array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
@@ -449,13 +428,8 @@ public function getUsedSpaceForUsers(array $users) {
return $user->getUID();
}, $users);
- $query = $builder->select('m.user_id', 'f.size')
+ $query = $builder->select('m.user_id', 'storage_id')
->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f',
- $builder->expr()->andX(
- $builder->expr()->eq('m.storage_id', 'f.storage'),
- $builder->expr()->eq('f.path_hash', $builder->createNamedParameter(md5('files')))
- ))
->where($builder->expr()->eq('m.mount_point', $mountPoint))
->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
@@ -463,12 +437,17 @@ public function getUsedSpaceForUsers(array $users) {
$results = [];
while ($row = $result->fetch()) {
- $results[$row['user_id']] = $row['size'];
+ $results[$row['user_id']] = $this->getSizeForHomeStorage($row['storage_id']);
}
$result->closeCursor();
return $results;
}
+ private function getSizeForHomeStorage(int $storage): int {
+ $entry = $this->cacheAccess->getByPathInStorage('files', $storage);
+ return $entry->getSize();
+ }
+
public function clear(): void {
$this->cacheInfoCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
diff --git a/tests/lib/Files/Config/UserMountCacheTest.php b/tests/lib/Files/Config/UserMountCacheTest.php
index 9e910f4f47f5b..ff83254af29f3 100644
--- a/tests/lib/Files/Config/UserMountCacheTest.php
+++ b/tests/lib/Files/Config/UserMountCacheTest.php
@@ -8,11 +8,14 @@
namespace Test\Files\Config;
+use OC\DB\Exceptions\DbalException;
use OC\DB\QueryBuilder\Literal;
+use OC\Files\Cache\FileAccess;
use OC\Files\Mount\MountPoint;
use OC\Files\Storage\Storage;
use OC\User\Manager;
use OCP\Cache\CappedMemoryCache;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\ICachedMountInfo;
@@ -20,6 +23,7 @@
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
+use OCP\Server;
use Psr\Log\LoggerInterface;
use Test\TestCase;
use Test\Util\User\Dummy;
@@ -28,10 +32,8 @@
* @group DB
*/
class UserMountCacheTest extends TestCase {
- /**
- * @var IDBConnection
- */
- private $connection;
+ private IDBConnection $connection;
+ private FileAccess $cacheAccess;
/**
* @var IUserManager
@@ -49,7 +51,8 @@ protected function setUp(): void {
parent::setUp();
$this->fileIds = [];
- $this->connection = \OC::$server->getDatabaseConnection();
+ $this->connection = Server::get(IDBConnection::class);
+ $this->cacheAccess = Server::get(FileAccess::class);
$config = $this->getMockBuilder(IConfig::class)
->disableOriginalConstructor()
->getMock();
@@ -67,7 +70,13 @@ protected function setUp(): void {
$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),
+ $this->cacheAccess,
+ );
}
protected function tearDown(): void {
@@ -336,31 +345,36 @@ private function sortMounts(&$mounts) {
private function createCacheEntry($internalPath, $storageId, $size = 0) {
$internalPath = trim($internalPath, '/');
- $inserted = $this->connection->insertIfNotExist('*PREFIX*filecache', [
- 'storage' => $storageId,
- 'path' => $internalPath,
- 'path_hash' => md5($internalPath),
- 'parent' => -1,
- 'name' => basename($internalPath),
- 'mimetype' => 0,
- 'mimepart' => 0,
- 'size' => $size,
- 'storage_mtime' => 0,
- 'encrypted' => 0,
- 'unencrypted_size' => 0,
- 'etag' => '',
- 'permissions' => 31
- ], ['storage', 'path_hash']);
- if ($inserted) {
- $id = (int)$this->connection->lastInsertId('*PREFIX*filecache');
+ $query = $this->connection->getQueryBuilder();
+ $query->insert('filecache')
+ ->values([
+ 'storage' => $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT),
+ 'path' => $query->createNamedParameter($internalPath),
+ 'path_hash' => $query->createNamedParameter(md5($internalPath)),
+ 'parent' => $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT),
+ 'name' => $query->createNamedParameter(basename($internalPath)),
+ 'mimetype' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
+ 'mimepart' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
+ 'size' => $query->createNamedParameter($size, IQueryBuilder::PARAM_INT),
+ 'storage_mtime' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
+ 'encrypted' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
+ 'unencrypted_size' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
+ 'etag' => $query->createNamedParameter(''),
+ 'permissions' => $query->createNamedParameter(31, IQueryBuilder::PARAM_INT),
+ ]);
+ try {
+ $query->executeStatement();
+ $id = (int)$query->getLastInsertId();
$this->fileIds[] = $id;
- } else {
- $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` =?';
- $query = $this->connection->prepare($sql);
- $query->execute([$storageId, md5($internalPath)]);
- return (int)$query->fetchOne();
+ return $id;
+ } catch (DbalException $e) {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('fileid')
+ ->from('filecache')
+ ->where($query->expr()->eq('storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
+ ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5($internalPath))));
+ return (int)$query->executeQuery()->fetchOne();
}
- return $id;
}
public function testGetMountsForFileIdRootId() {