From 6c48415d7ae9239b4a73b00c58d09b9685bb3307 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Fri, 9 Nov 2018 09:16:26 +0100 Subject: [PATCH] Catch and ignore UniqueConstraintViolationException in file locking init * caused by a concurrect insert that happens between the INSERT and it's sub-SELECT which was there to actually avoid it within insertIfNotExists - sub selects are not atomic and allow this * see also https://github.com/nextcloud/server/pull/12315 Avoids an error that has this stack trace: ``` Doctrine\DBAL\Exception\UniqueConstraintViolationException An exception occurred while executing 'INSERT INTO "oc_file_locks" ("key","lock","ttl") SELECT ?,?,? FROM "oc_file_locks" WHERE "key" = ? HAVING COUNT(*) = 0' with params ["files/737d52477f1fb583a5bfe5eb33e820da", 1, 1524066628, "files/737d52477f1fb583a5bfe5eb33e820da"]: SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "lock_key_index" DETAIL: Key (key)=(files/737d52477f1fb583a5bfe5eb33e820da) already exists. #0 3rdparty/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php(128): Doctrine\DBAL\Driver\AbstractPostgreSQLDriver->convertException('An exception oc...', Object(Doctrine\DBAL\Driver\PDOException)) #1 3rdparty/doctrine/dbal/lib/Doctrine/DBAL/Connection.php(1015): Doctrine\DBAL\DBALException::driverExceptionDuringQuery(Object(Doctrine\DBAL\Driver\PDOPgSql\Driver), Object(Doctrine\DBAL\Driver\PDOException), 'INSERT INTO "oc...', Array) #2 lib/private/DB/Connection.php(213): Doctrine\DBAL\Connection->executeUpdate('INSERT INTO "oc...', Array, Array) #3 lib/private/DB/Adapter.php(114): OC\DB\Connection->executeUpdate('INSERT INTO "oc...', Array)\n#4 lib/private/DB/Connection.php(251): OC\DB\Adapter->insertIfNotExist('*PREFIX*file_lo...', Array, Array)\n#5 lib/private/Lock/DBLockingProvider.php(118): OC\DB\Connection->insertIfNotExist('*PREFIX*file_lo...', Array, Array)\n#6 lib/private/Lock/DBLockingProvider.php(163): OC\Lock\DBLockingProvider->initLockField('files/737d52477...', 1) #7 lib/private/Files/Storage/Common.php(704): OC\Lock\DBLockingProvider->acquireLock('files/737d52477...', 1) #8 lib/private/Files/View.php(1931): OC\Files\Storage\Common->acquireLock('files/Documents', 1, Object(OC\Lock\DBLockingProvider)) #9 lib/private/Files/View.php(2041): OC\Files\View->lockPath('/*******/files/...', 1, false) #10 lib/private/Files/Node/Node.php(360): OC\Files\View->lockFile('/*******/files/...', 1) #11 apps/files_sharing/lib/Controller/ShareAPIController.php(928): OC\Files\Node\Node->lock(1) #12 apps/files_sharing/lib/Controller/ShareAPIController.php(589): OCA\Files_Sharing\Controller\ShareAPIController->lock(Object(OC\Files\Node\Folder)) #13 [internal function]: OCA\Files_Sharing\Controller\ShareAPIController->getShares('true', 'false', 'false', Object(OC\Files\Node\Folder), 'false') #14 lib/private/AppFramework/Http/Dispatcher.php(160): call_user_func_array(Array, Array) #15 lib/private/AppFramework/Http/Dispatcher.php(90): OC\AppFramework\Http\Dispatcher->executeController(Object(OCA\Files_Sharing\Controller\ShareAPIController), 'getShares') #16 lib/private/AppFramework/App.php(114): OC\AppFramework\Http\Dispatcher->dispatch(Object(OCA\Files_Sharing\Controller\ShareAPIController), 'getShares') #17 lib/private/AppFramework/Routing/RouteActionHandler.php(47): OC\AppFramework\App::main('OCA\\Files_Shari...', 'getShares', Object(OC\AppFramework\DependencyInjection\DIContainer), Array) #18 [internal function]: OC\AppFramework\Routing\RouteActionHandler->__invoke(Array) #19 lib/private/Route/Router.php(299): call_user_func(Object(OC\AppFramework\Routing\RouteActionHandler), Array) #20 ocs/v1.php(78): OC\Route\Router->match('/ocsapp/apps/fi...') #21 ocs/v2.php(23): require_once('/usr/share/weba...') #22 {main} ```` Signed-off-by: Morris Jobke --- lib/private/Lock/DBLockingProvider.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/private/Lock/DBLockingProvider.php b/lib/private/Lock/DBLockingProvider.php index 6adb7488217f7..78e7dfa697f8a 100644 --- a/lib/private/Lock/DBLockingProvider.php +++ b/lib/private/Lock/DBLockingProvider.php @@ -26,6 +26,7 @@ namespace OC\Lock; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OC\DB\QueryBuilder\Literal; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -133,7 +134,15 @@ public function __construct( protected function initLockField(string $path, int $lock = 0): int { $expire = $this->getExpireTime(); - return $this->connection->insertIfNotExist('*PREFIX*file_locks', ['key' => $path, 'lock' => $lock, 'ttl' => $expire], ['key']); + try { + return $this->connection->insertIfNotExist('*PREFIX*file_locks', ['key' => $path, 'lock' => $lock, 'ttl' => $expire], ['key']); + } catch (UniqueConstraintViolationException $e) { + // if this is thrown then a concurrent insert happened between the insert and the sub-select in the insert, that should have avoided it + // it's fine to ignore this then + // + // more discussions about this can be found at https://github.com/nextcloud/server/pull/12315 + return 0; + } } /**