aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2024-03-27 16:13:51 +0100
committerLouis <louis@chmn.me>2024-03-28 15:29:03 +0100
commitee14e2f965897c3847ec67b557c7987d09ba5b1c (patch)
tree9317fd91c14cc644dfa52ff30e4c9d9a8baedc4a /lib
parentf2063bf7755ed0d612c642eaeb58be36b5acd9b6 (diff)
downloadnextcloud-server-ee14e2f965897c3847ec67b557c7987d09ba5b1c.tar.gz
nextcloud-server-ee14e2f965897c3847ec67b557c7987d09ba5b1c.zip
feat(files)): Add retry logic to cover deadlock situations when moving many files
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'lib')
-rw-r--r--lib/private/Files/Cache/Cache.php31
1 files changed, 25 insertions, 6 deletions
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 938df9c475d..5a3e38dd569 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -40,6 +40,7 @@
namespace OC\Files\Cache;
+use Doctrine\DBAL\Exception\RetryableException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchQuery;
@@ -701,7 +702,6 @@ class Cache implements ICache {
throw new \Exception('Invalid target storage id: ' . $targetStorageId);
}
- $this->connection->beginTransaction();
if ($sourceData['mimetype'] === 'httpd/unix-directory') {
//update all child entries
$sourceLength = mb_strlen($sourcePath);
@@ -724,12 +724,31 @@ class Cache implements ICache {
$query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT));
}
- try {
- $query->execute();
- } catch (\OC\DatabaseException $e) {
- $this->connection->rollBack();
- throw $e;
+ // Retry transaction in case of RetryableException like deadlocks.
+ // Retry up to 4 times because we should receive up to 4 concurrent requests from the frontend
+ $retryLimit = 4;
+ for ($i = 1; $i <= $retryLimit; $i++) {
+ try {
+ $this->connection->beginTransaction();
+ $query->executeStatement();
+ break;
+ } catch (\OC\DatabaseException $e) {
+ $this->connection->rollBack();
+ throw $e;
+ } catch (RetryableException $e) {
+ // Simply throw if we already retried 4 times.
+ if ($i === $retryLimit) {
+ throw $e;
+ }
+
+ $this->connection->rollBack();
+
+ // Sleep a bit to give some time to the other transaction to finish.
+ usleep(100 * 1000 * $i);
+ }
}
+ } else {
+ $this->connection->beginTransaction();
}
$query = $this->getQueryBuilder();