mirror of
https://github.com/nextcloud/server.git
synced 2026-05-19 16:39:59 -04:00
Merge pull request #34447 from nextcloud/backport/34302/stable24
[stable24] Fix: Prevent deadlocks during mtime/size/etag propagation
This commit is contained in:
commit
9ca4d1368d
1 changed files with 30 additions and 19 deletions
|
|
@ -24,17 +24,19 @@
|
|||
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OC\DB\QueryBuilder\QueryFunction;
|
||||
use Doctrine\DBAL\Exception\RetryableException;
|
||||
use OC\Files\Storage\Wrapper\Encryption;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\Cache\IPropagator;
|
||||
use OCP\Files\Storage\IReliableEtagStorage;
|
||||
use OCP\IDBConnection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Propagate etags and mtimes within the storage
|
||||
*/
|
||||
class Propagator implements IPropagator {
|
||||
public const MAX_RETRIES = 3;
|
||||
private $inBatch = false;
|
||||
|
||||
private $batch = [];
|
||||
|
|
@ -101,35 +103,44 @@ class Propagator implements IPropagator {
|
|||
$builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR));
|
||||
}
|
||||
|
||||
$builder->execute();
|
||||
|
||||
if ($sizeDifference !== 0) {
|
||||
// we need to do size separably so we can ignore entries with uncalculated size
|
||||
$builder = $this->connection->getQueryBuilder();
|
||||
$builder->update('filecache')
|
||||
->set('size', $builder->func()->greatest(
|
||||
$builder->func()->add('size', $builder->createNamedParameter($sizeDifference)),
|
||||
$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
|
||||
))
|
||||
->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($builder->expr()->in('path_hash', $hashParams))
|
||||
->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT)));
|
||||
$hasCalculatedSize = $builder->expr()->gt('size', $builder->expr()->literal(-1, IQUeryBuilder::PARAM_INT));
|
||||
$sizeColumn = $builder->getColumnName('size');
|
||||
$newSize = $builder->func()->greatest(
|
||||
$builder->func()->add('size', $builder->createNamedParameter($sizeDifference)),
|
||||
$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
|
||||
);
|
||||
|
||||
// Only update if row had a previously calculated size
|
||||
$builder->set('size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newSize ELSE $sizeColumn END"));
|
||||
|
||||
if ($this->storage->instanceOfStorage(Encryption::class)) {
|
||||
// in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
|
||||
$eq = $builder->expr()->eq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT));
|
||||
$hasUnencryptedSize = $builder->expr()->neq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT));
|
||||
$sizeColumn = $builder->getColumnName('size');
|
||||
$unencryptedSizeColumn = $builder->getColumnName('unencrypted_size');
|
||||
$builder->set('unencrypted_size', $builder->func()->greatest(
|
||||
$newUnencryptedSize = $builder->func()->greatest(
|
||||
$builder->func()->add(
|
||||
new QueryFunction("CASE WHEN $eq THEN $unencryptedSizeColumn ELSE $sizeColumn END"),
|
||||
$builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $unencryptedSizeColumn ELSE $sizeColumn END"),
|
||||
$builder->createNamedParameter($sizeDifference)
|
||||
),
|
||||
$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
$builder->execute();
|
||||
// Only update if row had a previously calculated size
|
||||
$builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END"));
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < self::MAX_RETRIES; $i++) {
|
||||
try {
|
||||
$builder->executeStatement();
|
||||
break;
|
||||
} catch (RetryableException $e) {
|
||||
/** @var LoggerInterface $loggerInterface */
|
||||
$loggerInterface = \OC::$server->get(LoggerInterface::class);
|
||||
$loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue