perf(metadata): Add optimized sharding for metadata deletion

Signed-off-by: Carl Schwan <carlschwan@kde.org>
This commit is contained in:
Carl Schwan 2025-08-26 17:54:13 +02:00 committed by Carl Schwan
parent fd3878448b
commit 7100c71166
No known key found for this signature in database
GPG key ID: 02325448204E452A
7 changed files with 45 additions and 25 deletions

View file

@ -635,18 +635,32 @@ class Cache implements ICache {
} }
$cacheEntryRemovedEvents = []; $cacheEntryRemovedEvents = [];
foreach (array_combine($deletedIds, $deletedPaths) as $fileId => $filePath) { foreach (array_chunk(array_combine($deletedIds, $deletedPaths), 1000) as $chunk) {
$cacheEntryRemovedEvent = new CacheEntryRemovedEvent( /** @var array<int, string> $chunk */
$this->storage, foreach ($chunk as $fileId => $filePath) {
$filePath, $cacheEntryRemovedEvents[] = new CacheEntryRemovedEvent(
$fileId, $this->storage,
$this->getNumericStorageId() $filePath,
); $fileId,
$cacheEntryRemovedEvents[] = $cacheEntryRemovedEvent; $this->getNumericStorageId()
$this->eventDispatcher->dispatchTyped($cacheEntryRemovedEvent); );
} }
$this->eventDispatcher->dispatchTyped(new CacheEntriesRemovedEvent($cacheEntryRemovedEvents));
$exception = null;
try {
$this->eventDispatcher->dispatchTyped(new CacheEntriesRemovedEvent($cacheEntryRemovedEvents));
} catch (\Exception $e) {
// still send the other event
$exception = $e;
}
foreach ($cacheEntryRemovedEvents as $cacheEntryRemovedEvent) {
$this->eventDispatcher->dispatchTyped($cacheEntryRemovedEvent);
}
if ($exception !== null) {
throw $exception;
}
}
} }
/** /**

View file

@ -214,9 +214,9 @@ class FilesMetadataManager implements IFilesMetadataManager {
} }
} }
public function deleteMetadataForFiles(array $fileIds): void { public function deleteMetadataForFiles(int $storage, array $fileIds): void {
try { try {
$this->metadataRequestService->dropMetadataForFiles($fileIds); $this->metadataRequestService->dropMetadataForFiles($storage, $fileIds);
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->warning('issue while deleteMetadata', ['exception' => $e, 'fileIds' => $fileIds]); $this->logger->warning('issue while deleteMetadata', ['exception' => $e, 'fileIds' => $fileIds]);
} }

View file

@ -33,18 +33,21 @@ class MetadataDelete implements IEventListener {
} }
$entries = $event->getCacheEntryRemovedEvents(); $entries = $event->getCacheEntryRemovedEvents();
$fileIds = []; $storageToFileIds = [];
foreach ($entries as $entry) { foreach ($entries as $entry) {
try { try {
$fileIds[] = $entry->getFileId(); $storageToFileIds[$entry->getStorageId()] ??= [];
$storageToFileIds[$entry->getStorageId()][] = $entry->getFileId();
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->warning('issue while running MetadataDelete', ['exception' => $e]); $this->logger->warning('issue while running MetadataDelete', ['exception' => $e]);
} }
} }
try { try {
$this->filesMetadataManager->deleteMetadataForFiles($fileIds); foreach ($storageToFileIds as $storageId => $fileIds) {
$this->filesMetadataManager->deleteMetadataForFiles($storageId, $fileIds);
}
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->warning('issue while running MetadataDelete', ['exception' => $e]); $this->logger->warning('issue while running MetadataDelete', ['exception' => $e]);
} }

View file

@ -147,16 +147,16 @@ class MetadataRequestService {
/** /**
* @param int[] $fileIds * @param int[] $fileIds
* @return void
* @throws Exception * @throws Exception
*/ */
public function dropMetadataForFiles(array $fileIds): void { public function dropMetadataForFiles(int $storage, array $fileIds): void {
$chunks = array_chunk($fileIds, 1000); $chunks = array_chunk($fileIds, 1000);
foreach ($chunks as $chunk) { foreach ($chunks as $chunk) {
$qb = $this->dbConnection->getQueryBuilder(); $qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_METADATA) $qb->delete(self::TABLE_METADATA)
->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); ->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)))
->hintShardKey('storage', $storage);
$qb->executeStatement(); $qb->executeStatement();
} }
} }

View file

@ -8,26 +8,28 @@ declare(strict_types=1);
*/ */
namespace OCP\Files\Cache; namespace OCP\Files\Cache;
use OCP\AppFramework\Attribute\Listenable;
use OCP\EventDispatcher\Event; use OCP\EventDispatcher\Event;
/** /**
* Meta-event wrapping multiple CacheEntryRemovedEvent for when an existing * Meta-event wrapping multiple CacheEntryRemovedEvent for when an existing
* entry in the cache gets removed. * entry in the cache gets removed.
* *
* @since 32.0.0 * @since 34.0.0
*/ */
#[\OCP\AppFramework\Attribute\Listenable(since: '32.0.0')] #[Listenable(since: '34.0.0')]
class CacheEntriesRemovedEvent extends Event { class CacheEntriesRemovedEvent extends Event {
/** /**
* @param CacheEntryRemovedEvent[] $cacheEntryRemovedEvents * @param ICacheEvent[] $cacheEntryRemovedEvents
*/ */
public function __construct( public function __construct(
private readonly array $cacheEntryRemovedEvents, private readonly array $cacheEntryRemovedEvents,
) { ) {
Event::__construct();
} }
/** /**
* @return CacheEntryRemovedEvent[] * @return ICacheEvent[]
*/ */
public function getCacheEntryRemovedEvents(): array { public function getCacheEntryRemovedEvents(): array {
return $this->cacheEntryRemovedEvents; return $this->cacheEntryRemovedEvents;

View file

@ -11,7 +11,7 @@ namespace OCP\Files\Cache;
/** /**
* Event for when an existing entry in the cache gets removed * Event for when an existing entry in the cache gets removed
* *
* Prefer using \c CacheEntriesRemovedEvent as it is more efficient when deleting * Prefer using CacheEntriesRemovedEvent as it is more efficient when deleting
* multiple files at the same time. * multiple files at the same time.
* *
* @since 21.0.0 * @since 21.0.0

View file

@ -103,11 +103,12 @@ interface IFilesMetadataManager {
/** /**
* Delete metadata and its indexes of multiple file ids * Delete metadata and its indexes of multiple file ids
* *
* @param int $storage The storage id coresponding to the $fileIds
* @param array<int> $fileIds file ids * @param array<int> $fileIds file ids
* @return void * @return void
* @since 32.0.0 * @since 32.0.0
*/ */
public function deleteMetadataForFiles(array $fileIds): void; public function deleteMetadataForFiles(int $storage, array $fileIds): void;
/** /**
* generate and return a MetadataQuery to help building sql queries * generate and return a MetadataQuery to help building sql queries