Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorblizzz <blizzz@arthur-schiwon.de>2022-10-05 13:52:29 +0300
committerGitHub <noreply@github.com>2022-10-05 13:52:29 +0300
commit0612e008d44bfe0441fcda0ffab1725e0ad73205 (patch)
treebdedd72720416cba1140bcf0386b2dcefac845f9
parentc765dac63354c28f9a85b5ecc9a425ab0aae2ce4 (diff)
parent7ed5679f2c015ea4f1bb5cd9668cee3e145a10c8 (diff)
Merge pull request #34405 from nextcloud/backport/34302/stable25
[stable25] Fix: Prevent deadlocks during mtime/size/etag propagation
-rw-r--r--lib/private/Files/Cache/Propagator.php46
1 files changed, 29 insertions, 17 deletions
diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php
index 2909e998bf9..4bf88a60843 100644
--- a/lib/private/Files/Cache/Propagator.php
+++ b/lib/private/Files/Cache/Propagator.php
@@ -24,16 +24,19 @@
namespace OC\Files\Cache;
+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 = [];
@@ -100,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(
- $builder->createFunction("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)
- ));
+ );
+
+ // Only update if row had a previously calculated size
+ $builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END"));
}
+ }
- $builder->execute();
+ for ($i = 0; $i < self::MAX_RETRIES; $i++) {
+ try {
+ $builder->executeStatement();
+ break;
+ } catch (RetryableException $e) {
+ /** @var LoggerInterface $loggerInterface */
+ $loggerInterface = \OCP\Server::get(LoggerInterface::class);
+ $loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]);
+ }
}
}