Skip to content

Commit

Permalink
fix: make delete orphan job compatible with sharding
Browse files Browse the repository at this point in the history
Signed-off-by: Robin Appelman <robin@icewind.nl>
  • Loading branch information
icewind1991 committed Aug 19, 2024
1 parent 2405452 commit 26b8d8c
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 45 deletions.
101 changes: 65 additions & 36 deletions apps/files/lib/Command/DeleteOrphanedFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,27 @@ protected function configure(): void {
public function execute(InputInterface $input, OutputInterface $output): int {
$deletedEntries = 0;

$query = $this->connection->getQueryBuilder();
$query->select('fc.fileid')
->from('filecache', 'fc')
->where($query->expr()->isNull('s.numeric_id'))
->leftJoin('fc', 'storages', 's', $query->expr()->eq('fc.storage', 's.numeric_id'))
->setMaxResults(self::CHUNK_SIZE);
$deletedStorages = array_diff($this->getReferencedStorages(), $this->getExistingStorages());

$deleteExtended = !$input->getOption('skip-filecache-extended');
if ($deleteExtended) {
$fileIdsByStorage = $this->getFileIdsForStorages($deletedStorages);
}

$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('filecache')
->where($deleteQuery->expr()->eq('fileid', $deleteQuery->createParameter('objectid')));
->where($deleteQuery->expr()->in('storage', $deleteQuery->createParameter('storage_ids')));

$deletedInLastChunk = self::CHUNK_SIZE;
while ($deletedInLastChunk === self::CHUNK_SIZE) {
$deletedInLastChunk = 0;
$result = $query->execute();
while ($row = $result->fetch()) {
$deletedInLastChunk++;
$deletedEntries += $deleteQuery->setParameter('objectid', (int) $row['fileid'])
->execute();
}
$result->closeCursor();
$deletedStorageChunks = array_chunk($deletedStorages, self::CHUNK_SIZE);
foreach ($deletedStorageChunks as $deletedStorageChunk) {
$deleteQuery->setParameter('storage_ids', $deletedStorageChunk, IQueryBuilder::PARAM_INT_ARRAY);
$deletedEntries += $deleteQuery->executeStatement();
}

$output->writeln("$deletedEntries orphaned file cache entries deleted");

if (!$input->getOption('skip-filecache-extended')) {
$deletedFileCacheExtended = $this->cleanupOrphanedFileCacheExtended();
if ($deleteExtended) {
$deletedFileCacheExtended = $this->cleanupOrphanedFileCacheExtended($fileIdsByStorage);

Check notice

Code scanning / Psalm

PossiblyUndefinedVariable Note

Possibly undefined variable $fileIdsByStorage, first seen on line 43
$output->writeln("$deletedFileCacheExtended orphaned file cache extended entries deleted");
}

Expand All @@ -72,28 +66,63 @@ public function execute(InputInterface $input, OutputInterface $output): int {
return self::SUCCESS;
}

private function cleanupOrphanedFileCacheExtended(): int {
$deletedEntries = 0;

private function getReferencedStorages(): array {
$query = $this->connection->getQueryBuilder();
$query->select('fce.fileid')
->from('filecache_extended', 'fce')
->leftJoin('fce', 'filecache', 'fc', $query->expr()->eq('fce.fileid', 'fc.fileid'))
->where($query->expr()->isNull('fc.fileid'))
->setMaxResults(self::CHUNK_SIZE);
$query->select('storage')
->from('filecache')
->groupBy('storage')
->runAcrossAllShards();
return $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
}

$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('filecache_extended')
->where($deleteQuery->expr()->in('fileid', $deleteQuery->createParameter('idsToDelete')));
private function getExistingStorages(): array {
$query = $this->connection->getQueryBuilder();
$query->select('numeric_id')
->from('storages')
->groupBy('numeric_id');
return $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
}

$result = $query->executeQuery();
while ($result->rowCount() > 0) {
$idsToDelete = $result->fetchAll(\PDO::FETCH_COLUMN);
/**
* @param int[] $storageIds
* @return array<int, int[]>
*/
private function getFileIdsForStorages(array $storageIds): array {
$query = $this->connection->getQueryBuilder();
$query->select('storage', 'fileid')
->from('filecache')
->where($query->expr()->in('storage', $query->createParameter('storage_ids')));

$result = [];
$storageIdChunks = array_chunk($storageIds, self::CHUNK_SIZE);
foreach ($storageIdChunks as $storageIdChunk) {
$query->setParameter('storage_ids', $storageIdChunk, IQueryBuilder::PARAM_INT_ARRAY);
$chunk = $query->executeQuery()->fetchAll();
foreach ($chunk as $row) {
$result[$row['storage']][] = $row['fileid'];
}
}
return $result;
}

$deleteQuery->setParameter('idsToDelete', $idsToDelete, IQueryBuilder::PARAM_INT_ARRAY);
$deletedEntries += $deleteQuery->executeStatement();
/**
* @param array<int, int[]> $fileIdsByStorage
* @return int
*/
private function cleanupOrphanedFileCacheExtended(array $fileIdsByStorage): int {
$deletedEntries = 0;

$result = $query->executeQuery();
$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('filecache_extended')
->where($deleteQuery->expr()->in('fileid', $deleteQuery->createParameter('file_ids')));

foreach ($fileIdsByStorage as $storageId => $fileIds) {
$deleteQuery->hintShardKey('storage', $storageId, true);
$fileChunks = array_chunk($fileIds, self::CHUNK_SIZE);
foreach ($fileChunks as $fileChunk) {
$deleteQuery->setParameter('file_ids', $fileChunk, IQueryBuilder::PARAM_INT_ARRAY);
$deletedEntries += $deleteQuery->executeStatement();
}
}

return $deletedEntries;
Expand Down
14 changes: 10 additions & 4 deletions apps/files/tests/Command/DeleteOrphanedFilesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ protected function tearDown(): void {
}

protected function getFile($fileId) {
$stmt = $this->connection->executeQuery('SELECT * FROM `*PREFIX*filecache` WHERE `fileid` = ?', [$fileId]);
return $stmt->fetchAll();
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('filecache')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId)));
return $query->executeQuery()->fetchAll();
}

protected function getMounts($storageId) {
$stmt = $this->connection->executeQuery('SELECT * FROM `*PREFIX*mounts` WHERE `storage_id` = ?', [$storageId]);
return $stmt->fetchAll();
$query = $this->connection->getQueryBuilder();
$query->select('*')
->from('mounts')
->where($query->expr()->eq('storage_id', $query->createNamedParameter($storageId)));
return $query->executeQuery()->fetchAll();
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/private/DB/QueryBuilder/ExtendedQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ public function executeStatement(?IDBConnection $connection = null): int {
return $this->builder->executeStatement($connection);
}

public function hintShardKey(string $column, mixed $value) {
$this->builder->hintShardKey($column, $value);
public function hintShardKey(string $column, mixed $value, bool $overwrite = false) {
$this->builder->hintShardKey($column, $value, $overwrite);
return $this;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/private/DB/QueryBuilder/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,7 @@ public function quoteAlias($alias) {
return $this->helper->quoteColumnName($alias);
}

public function hintShardKey(string $column, mixed $value) {
public function hintShardKey(string $column, mixed $value, bool $overwrite = false) {
return $this;
}

Expand Down
6 changes: 5 additions & 1 deletion lib/private/DB/QueryBuilder/Sharded/ShardedQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,11 @@ private function registerOrder(string $column, string $order): void {
];
}

public function hintShardKey(string $column, mixed $value) {
public function hintShardKey(string $column, mixed $value, bool $overwrite = false) {
if ($overwrite) {
$this->primaryKeys = [];
$this->shardKeys = [];
}
if ($this->shardDefinition?->isKey($column)) {
$this->primaryKeys[] = $value;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/public/DB/QueryBuilder/IQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ public function getColumnName($column, $tableAlias = '');
* @return $this
* @since 30.0.0
*/
public function hintShardKey(string $column, mixed $value);
public function hintShardKey(string $column, mixed $value, bool $overwrite = false);

/**
* Set the query to run across all shards if sharding is enabled.
Expand Down

0 comments on commit 26b8d8c

Please sign in to comment.