diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2023-12-06 13:52:37 +0100 |
---|---|---|
committer | skjnldsv <skjnldsv@protonmail.com> | 2024-11-06 09:13:01 +0100 |
commit | 0bb68ab734016fe69976a29e3c93f46f2c7e011c (patch) | |
tree | 48cc0295885831af71b1708e96aed392903ab28a /apps | |
parent | 452e4be4f5e27d76f3cb52b9016c0dbbd989841a (diff) | |
download | nextcloud-server-0bb68ab734016fe69976a29e3c93f46f2c7e011c.tar.gz nextcloud-server-0bb68ab734016fe69976a29e3c93f46f2c7e011c.zip |
fix(carddav): Handle race for SAB creation better
* Accept non repeatable read and INSERT conflict by another read
* Handle replication edge case
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/dav/lib/CardDAV/CardDavBackend.php | 1 | ||||
-rw-r--r-- | apps/dav/lib/CardDAV/SyncService.php | 33 |
2 files changed, 27 insertions, 7 deletions
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index f7d0a888efd..b15ed607076 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -351,6 +351,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { * @param array $properties * @return int * @throws BadRequest + * @throws Exception */ public function createAddressBook($principalUri, $url, array $properties) { if (strlen($url) > 255) { diff --git a/apps/dav/lib/CardDAV/SyncService.php b/apps/dav/lib/CardDAV/SyncService.php index 4e8c4b1355e..cc3d324faf1 100644 --- a/apps/dav/lib/CardDAV/SyncService.php +++ b/apps/dav/lib/CardDAV/SyncService.php @@ -10,6 +10,7 @@ namespace OCA\DAV\CardDAV; use OCP\AppFramework\Db\TTransactional; use OCP\AppFramework\Http; +use OCP\DB\Exception; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\IConfig; @@ -89,15 +90,33 @@ class SyncService { * @throws \Sabre\DAV\Exception\BadRequest */ public function ensureSystemAddressBookExists(string $principal, string $uri, array $properties): ?array { - return $this->atomic(function () use ($principal, $uri, $properties) { - $book = $this->backend->getAddressBooksByUri($principal, $uri); - if (!is_null($book)) { - return $book; + try { + return $this->atomic(function () use ($principal, $uri, $properties) { + $book = $this->backend->getAddressBooksByUri($principal, $uri); + if (!is_null($book)) { + return $book; + } + $this->backend->createAddressBook($principal, $uri, $properties); + + return $this->backend->getAddressBooksByUri($principal, $uri); + }, $this->dbConnection); + } catch (Exception $e) { + // READ COMMITTED doesn't prevent a nonrepeatable read above, so + // two processes might create an address book here. Ignore our + // failure and continue loading the entry written by the other process + if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; } - $this->backend->createAddressBook($principal, $uri, $properties); - return $this->backend->getAddressBooksByUri($principal, $uri); - }, $this->dbConnection); + // If this fails we might have hit a replication node that does not + // have the row written in the other process. + // TODO: find an elegant way to handle this + $ab = $this->backend->getAddressBooksByUri($principal, $uri); + if ($ab === null) { + throw new Exception('Could not create system address book', $e->getCode(), $e); + } + return $ab; + } } private function prepareUri(string $host, string $path): string { |