Skip to content

Commit

Permalink
Catch and ignore UniqueConstraintViolationException in file locking init
Browse files Browse the repository at this point in the history
* 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 #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 <hey@morrisjobke.de>
  • Loading branch information
MorrisJobke committed Nov 9, 2018
1 parent da57aaf commit 6c48415
Showing 1 changed file with 10 additions and 1 deletion.
11 changes: 10 additions & 1 deletion lib/private/Lock/DBLockingProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

/**
Expand Down

0 comments on commit 6c48415

Please sign in to comment.