|
@@ -26,8 +26,10 @@
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
|
*
|
|
|
*/
|
|
|
+
|
|
|
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 @@ use Psr\Log\LoggerInterface;
|
|
|
* 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<ICachedMountInfo[]>
|
|
|
**/
|
|
|
private CappedMemoryCache $mountsForUsers;
|
|
|
/**
|
|
|
* fileid => internal path mapping for cached mount info.
|
|
|
+ *
|
|
|
* @var CappedMemoryCache<string>
|
|
|
**/
|
|
|
private CappedMemoryCache $internalPathCache;
|
|
|
- private LoggerInterface $logger;
|
|
|
/** @var CappedMemoryCache<array> */
|
|
|
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 @@ class UserMountCache implements IUserMountCache {
|
|
|
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 @@ class UserMountCache implements IUserMountCache {
|
|
|
* @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 @@ class UserMountCache implements IUserMountCache {
|
|
|
$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 @@ class UserMountCache implements IUserMountCache {
|
|
|
*/
|
|
|
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 @@ class UserMountCache implements IUserMountCache {
|
|
|
* @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 @@ class UserMountCache implements IUserMountCache {
|
|
|
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 @@ class UserMountCache implements IUserMountCache {
|
|
|
|
|
|
$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();
|