fix(UserMountCache): Add back unique index for oc_mounts and use normal insert

Signed-off-by: provokateurin <kate@provokateurin.de>
This commit is contained in:
provokateurin 2025-12-09 13:53:12 +01:00
parent 2a96020bb8
commit 3d113daa60
No known key found for this signature in database
8 changed files with 97 additions and 37 deletions

View file

@ -186,12 +186,6 @@ class AddMissingIndicesListener implements IEventListener {
'mounts_class_index',
['mount_provider_class']
);
$event->addMissingIndex(
'mounts',
'mounts_user_root_path_index',
['user_id', 'root_id', 'mount_point'],
['lengths' => [null, null, 128]]
);
$event->addMissingIndex(
'systemtag_object_mapping',

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Core\Migrations;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
class Version33000Date20251209123503 extends SimpleMigrationStep {
public function __construct(
private readonly IDBConnection $connection,
) {
}
#[Override]
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
$this->connection->truncateTable('mounts', false);
}
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('mounts');
if (!$table->hasColumn('mount_point_hash')) {
$table->addColumn('mount_point_hash', Types::STRING, [
'notnull' => true,
'length' => 32, // xxh128
]);
$table->dropIndex('mounts_user_root_path_index');
$table->addUniqueIndex(['user_id', 'root_id', 'mount_point_hash'], 'mounts_user_root_path_index');
return $schema;
}
return null;
}
}

View file

@ -1529,6 +1529,7 @@ return array(
'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php',
'OC\\Core\\Migrations\\Version32000Date20250731062008' => $baseDir . '/core/Migrations/Version32000Date20250731062008.php',
'OC\\Core\\Migrations\\Version32000Date20250806110519' => $baseDir . '/core/Migrations/Version32000Date20250806110519.php',
'OC\\Core\\Migrations\\Version33000Date20251209123503' => $baseDir . '/core/Migrations/Version33000Date20251209123503.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',

View file

@ -11,32 +11,32 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
);
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OC\\Core\\' => 8,
'OC\\' => 3,
'OCP\\' => 4,
),
'N' =>
'N' =>
array (
'NCU\\' => 4,
),
);
public static $prefixDirsPsr4 = array (
'OC\\Core\\' =>
'OC\\Core\\' =>
array (
0 => __DIR__ . '/../../..' . '/core',
),
'OC\\' =>
'OC\\' =>
array (
0 => __DIR__ . '/../../..' . '/lib/private',
),
'OCP\\' =>
'OCP\\' =>
array (
0 => __DIR__ . '/../../..' . '/lib/public',
),
'NCU\\' =>
'NCU\\' =>
array (
0 => __DIR__ . '/../../..' . '/lib/unstable',
),
@ -1570,6 +1570,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php',
'OC\\Core\\Migrations\\Version32000Date20250731062008' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250731062008.php',
'OC\\Core\\Migrations\\Version32000Date20250806110519' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250806110519.php',
'OC\\Core\\Migrations\\Version33000Date20251209123503' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251209123503.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',

View file

@ -9,6 +9,7 @@ namespace OC\Files\Config;
use OC\User\LazyUser;
use OCP\Cache\CappedMemoryCache;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
@ -164,14 +165,25 @@ class UserMountCache implements IUserMountCache {
private function addToCache(ICachedMountInfo $mount) {
if ($mount->getStorageId() !== -1) {
$this->connection->insertIfNotExist('*PREFIX*mounts', [
'storage_id' => $mount->getStorageId(),
'root_id' => $mount->getRootId(),
'user_id' => $mount->getUser()->getUID(),
'mount_point' => $mount->getMountPoint(),
'mount_id' => $mount->getMountId(),
'mount_provider_class' => $mount->getMountProvider(),
], ['root_id', 'user_id', 'mount_point']);
$qb = $this->connection->getQueryBuilder();
$qb
->insert('mounts')
->values([
'storage_id' => $qb->createNamedParameter($mount->getStorageId(), IQueryBuilder::PARAM_INT),
'root_id' => $qb->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT),
'user_id' => $qb->createNamedParameter($mount->getUser()->getUID()),
'mount_point' => $qb->createNamedParameter($mount->getMountPoint()),
'mount_point_hash' => $qb->createNamedParameter(hash('xxh128', $mount->getMountPoint())),
'mount_id' => $qb->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT),
'mount_provider_class' => $qb->createNamedParameter($mount->getMountProvider()),
]);
try {
$qb->executeStatement();
} catch (Exception $e) {
if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $e;
}
}
} else {
// in some cases this is legitimate, like orphaned shares
$this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint());
@ -184,6 +196,7 @@ class UserMountCache implements IUserMountCache {
$query = $builder->update('mounts')
->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
->set('mount_point_hash', $builder->createNamedParameter(hash('xxh128', $mount->getMountPoint())))
->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
->set('mount_provider_class', $builder->createNamedParameter($mount->getMountProvider()))
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
@ -198,7 +211,7 @@ class UserMountCache implements IUserMountCache {
$query = $builder->delete('mounts')
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)))
->andWhere($builder->expr()->eq('mount_point', $builder->createNamedParameter($mount->getMountPoint())));
->andWhere($builder->expr()->eq('mount_point_hash', $builder->createNamedParameter(hash('xxh128', $mount->getMountPoint()))));
$query->executeStatement();
}
@ -449,16 +462,8 @@ class UserMountCache implements IUserMountCache {
public function getUsedSpaceForUsers(array $users) {
$builder = $this->connection->getQueryBuilder();
$slash = $builder->createNamedParameter('/');
$mountPoint = $builder->func()->concat(
$builder->func()->concat($slash, 'user_id'),
$slash
);
$userIds = array_map(function (IUser $user) {
return $user->getUID();
}, $users);
$mountPointHashes = array_map(static fn (IUser $user) => hash('xxh128', '/' . $user->getUID() . '/'), $users);
$userIds = array_map(static fn (IUser $user) => $user->getUID(), $users);
$query = $builder->select('m.user_id', 'f.size')
->from('mounts', 'm')
@ -467,7 +472,7 @@ class UserMountCache implements IUserMountCache {
$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))
->where($builder->expr()->in('m.mount_point_hash', $builder->createNamedParameter($mountPointHashes, IQueryBuilder::PARAM_STR_ARRAY)))
->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
$result = $query->executeQuery();

View file

@ -87,6 +87,7 @@ class PartitionedQueryBuilderTest extends TestCase {
'storage_id' => $query->createNamedParameter(1001001, IQueryBuilder::PARAM_INT),
'user_id' => $query->createNamedParameter('partitioned_test'),
'mount_point' => $query->createNamedParameter('/mount/point'),
'mount_point_hash' => $query->createNamedParameter(hash('xxh128', '/mount/point')),
'mount_provider_class' => $query->createNamedParameter('test'),
'root_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
]);
@ -135,7 +136,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
// query borrowed from UserMountCache
$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_point_hash', 'mount_id', 'f.path', '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->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
@ -148,6 +149,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$this->assertCount(1, $results);
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
$this->assertEquals($results[0]['mount_point'], '/mount/point');
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
$this->assertEquals($results[0]['mount_provider_class'], 'test');
$this->assertEquals($results[0]['path'], 'file1');
}
@ -156,7 +158,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$builder = $this->getQueryBuilder();
$builder->addPartition(new PartitionSplit('filecache', ['filecache', 'filecache_extended']));
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class', 'fe.upload_time')
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class', 'fe.upload_time')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
->innerJoin('f', 'filecache_extended', 'fe', $builder->expr()->eq('f.fileid', 'fe.fileid'))
@ -170,6 +172,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$this->assertCount(1, $results);
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
$this->assertEquals($results[0]['mount_point'], '/mount/point');
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
$this->assertEquals($results[0]['mount_provider_class'], 'test');
$this->assertEquals($results[0]['path'], 'file1');
$this->assertEquals($results[0]['upload_time'], 1234);
@ -179,7 +182,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$builder = $this->getQueryBuilder();
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
$query = $builder->select('storage', 'm.root_id', 'm.user_id', 'm.mount_point', 'm.mount_id', 'path', 'm.mount_provider_class')
$query = $builder->select('storage', 'm.root_id', 'm.user_id', 'm.mount_point', 'm.mount_point_hash', 'm.mount_id', 'path', 'm.mount_provider_class')
->from('filecache', 'f')
->innerJoin('f', 'mounts', 'm', $builder->expr()->eq('m.root_id', 'f.fileid'));
$query->where($builder->expr()->eq('storage', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
@ -192,6 +195,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$this->assertCount(1, $results);
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
$this->assertEquals($results[0]['mount_point'], '/mount/point');
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
$this->assertEquals($results[0]['mount_provider_class'], 'test');
$this->assertEquals($results[0]['path'], 'file1');
}
@ -201,7 +205,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$builder->addPartition(new PartitionSplit('filecache', ['filecache']));
// query borrowed from UserMountCache
$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_point_hash', 'mount_id', 'f.path', 'mount_provider_class')
->selectAlias('s.id', 'storage_string_id')
->from('mounts', 'm')
->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
@ -216,6 +220,7 @@ class PartitionedQueryBuilderTest extends TestCase {
$this->assertCount(1, $results);
$this->assertEquals($results[0]['user_id'], 'partitioned_test');
$this->assertEquals($results[0]['mount_point'], '/mount/point');
$this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
$this->assertEquals($results[0]['mount_provider_class'], 'test');
$this->assertEquals($results[0]['path'], 'file1');
$this->assertEquals($results[0]['storage_string_id'], 'test1');

View file

@ -64,6 +64,7 @@ class FileAccessTest extends TestCase {
'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
'mount_point' => $queryBuilder->createNamedParameter('/files'),
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/files')),
'user_id' => $queryBuilder->createNamedParameter('test'),
])
->executeStatement();
@ -74,6 +75,7 @@ class FileAccessTest extends TestCase {
'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
'mount_point' => $queryBuilder->createNamedParameter('/documents'),
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/documents')),
'user_id' => $queryBuilder->createNamedParameter('test'),
])
->executeStatement();
@ -84,6 +86,7 @@ class FileAccessTest extends TestCase {
'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/foobar')),
'user_id' => $queryBuilder->createNamedParameter('test'),
])
->executeStatement();
@ -149,6 +152,7 @@ class FileAccessTest extends TestCase {
'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
'mount_provider_class' => $queryBuilder->createNamedParameter(LocalHomeMountProvider::class),
'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/home/user')),
'user_id' => $queryBuilder->createNamedParameter('test'),
])
->executeStatement();
@ -161,6 +165,7 @@ class FileAccessTest extends TestCase {
'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/test/files/foobar')),
'user_id' => $queryBuilder->createNamedParameter('test'),
])
->executeStatement();

View file

@ -9,7 +9,7 @@
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level
// when updating major/minor version number.
$OC_Version = [32, 0, 3, 2];
$OC_Version = [32, 0, 3, 3];
// The human-readable string
$OC_VersionString = '32.0.3';