aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis <louis@chmn.me>2024-03-28 16:45:32 +0100
committerGitHub <noreply@github.com>2024-03-28 16:45:32 +0100
commita357c065bd1c86dae196a1bd4759f183dd1f2733 (patch)
tree4f903a5e313194b90017e33493f8a149561e56b5
parent79be101eb7783563d935a0874b55001c64ca20d5 (diff)
parentad2b367dbd7b4aac51f4d32bf7f60f0878053352 (diff)
downloadnextcloud-server-a357c065bd1c86dae196a1bd4759f183dd1f2733.tar.gz
nextcloud-server-a357c065bd1c86dae196a1bd4759f183dd1f2733.zip
Merge pull request #44551 from nextcloud/backport/44504/stable28
[stable28] Add retry logic to cover deadlock situations during move operations
-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 052b3c75ce8..cd88aebb17d 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;
@@ -695,7 +696,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);
@@ -718,12 +718,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();