aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files/Cache
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2024-03-27 16:13:51 +0100
committerLouis Chemineau <louis@chmn.me>2024-03-28 09:05:47 +0100
commit751795999af9805e5793e97fac3d0563d3fac5c0 (patch)
tree34030d32e0aaa984daa95ca8037ad2219d4d75cf /lib/private/Files/Cache
parent9afcec721fdc75c876bb62cc2de8295280ed4810 (diff)
downloadnextcloud-server-751795999af9805e5793e97fac3d0563d3fac5c0.tar.gz
nextcloud-server-751795999af9805e5793e97fac3d0563d3fac5c0.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/private/Files/Cache')
-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 2c53706189a..5df4002530f 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;
@@ -692,7 +693,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);
@@ -715,12 +715,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();