From 9ecdf4ddc0e825a2ea61d2818d9b77116b0826fb Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Fri, 9 Nov 2018 09:52:28 +0100 Subject: [PATCH] Catch and ignore UniqueConstraintViolationException in file cache insert * 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_filecache" ("mimepart","mimetype","mtime","size","etag","storage_mtime","permissions","checksum","path_hash","path","parent","name","storage") SELECT ?,?,?,?,?,?,?,?,?,?,?,?,? FROM "oc_filecache" WHERE "storage" = ? AND "path_hash" = ? HAVING COUNT(*) = 0' with params [1, 2, 1502911047, -1, "59949a47cabae", 1502911047, 31, "", "fb66dca5f27af6f15c1d1d81e6f8d28b", "files_trashbin", 1742774, "files_trashbin", 136, 136, "fb66dca5f27af6f15c1d1d81e6f8d28b"]: SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "fs_storage_path_hash" DETAIL: Key (storage, path_hash)=(136, fb66dca5f27af6f15c1d1d81e6f8d28b) already exists. 3rdparty/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php - line 128: Doctrine\DBAL\Driver\AbstractPostgreSQLDriver->convertException('An exception oc...', Object(Doctrine\DBAL\Driver\PDOException)) 3rdparty/doctrine/dbal/lib/Doctrine/DBAL/Connection.php - line 1015: Doctrine\DBAL\DBALException driverExceptionDuringQuery(Object(Doctrine\DBAL\Driver\PDOPgSql\Driver), Object(Doctrine\DBAL\Driver\PDOException), 'INSERT INTO "oc...', Array) lib/private/DB/Connection.php - line 213: Doctrine\DBAL\Connection->executeUpdate('INSERT INTO "oc...', Array, Array) lib/private/DB/Adapter.php - line 114: OC\DB\Connection->executeUpdate('INSERT INTO "oc...', Array) lib/private/DB/Connection.php - line 251: OC\DB\Adapter->insertIfNotExist('*PREFIX*filecac...', Array, Array) lib/private/Files/Cache/Cache.php - line 273: OC\DB\Connection->insertIfNotExist('*PREFIX*filecac...', Array, Array) lib/private/Files/Cache/Cache.php - line 230: OC\Files\Cache\Cache->insert('files_trashbin', Array) lib/private/Files/Cache/Scanner.php - line 279: OC\Files\Cache\Cache->put('files_trashbin', Array) lib/private/Files/Cache/Scanner.php - line 216: OC\Files\Cache\Scanner->addToCache('files_trashbin', Array, -1) lib/private/Files/Cache/Scanner.php - line 175: OC\Files\Cache\Scanner->scanFile('files_trashbin') lib/private/Files/Cache/Scanner.php - line 322: OC\Files\Cache\Scanner->scanFile('files_trashbin/...', 3, -1, NULL, false) lib/private/Files/Cache/Updater.php - line 124: OC\Files\Cache\Scanner->scan('files_trashbin/...', false, 3, false) lib/private/Files/View.php - line 321: OC\Files\Cache\Updater->update('files_trashbin/...', 1502911047) lib/private/Files/View.php - line 1151: OC\Files\View->writeUpdate(Object(OCA\Files_Trashbin\Storage), 'files_trashbin/...') lib/private/Files/View.php - line 269: OC\Files\View->basicOperation('mkdir', '/files_trashbin...', Array) apps/files_trashbin/lib/Trashbin.php - line 154: OC\Files\View->mkdir('files_trashbin/...') apps/files_trashbin/lib/Trashbin.php - line 225: OCA\Files_Trashbin\Trashbin setUpTrash('klas') apps/files_trashbin/lib/Storage.php - line 247: OCA\Files_Trashbin\Trashbin move2trash('Photos', false) apps/files_trashbin/lib/Storage.php - line 189: OCA\Files_Trashbin\Storage->doDelete('files/Photos', 'rmdir') lib/private/Files/View.php - line 1136: OCA\Files_Trashbin\Storage->rmdir('files/Photos') lib/private/Files/View.php - line 348: OC\Files\View->basicOperation('rmdir', '/Photos', Array) apps/dav/lib/Connector/Sabre/Directory.php - line 303: OC\Files\View->rmdir('/Photos') 3rdparty/sabre/dav/lib/DAV/Tree.php - line 179: OCA\DAV\Connector\Sabre\Directory->delete() 3rdparty/sabre/dav/lib/DAV/CorePlugin.php - line 287: Sabre\DAV\Tree->delete('Photos') [internal function] Sabre\DAV\CorePlugin->httpDelete(Object(Sabre\HTTP\Request), Object(Sabre\HTTP\Response)) 3rdparty/sabre/event/lib/EventEmitterTrait.php - line 105: call_user_func_array(Array, Array) 3rdparty/sabre/dav/lib/DAV/Server.php - line 479: Sabre\Event\EventEmitter->emit('method DELETE', Array) 3rdparty/sabre/dav/lib/DAV/Server.php - line 254: Sabre\DAV\Server->invokeMethod(Object(Sabre\HTTP\Request), Object(Sabre\HTTP\Response)) apps/dav/appinfo/v1/webdav.php - line 71: Sabre\DAV\Server->exec() remote.php - line 162: require_once('/var/www/nextcl...') {main} ``` Signed-off-by: Morris Jobke --- lib/private/Files/Cache/Cache.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 007bccf0a54bb..4298c56eb51ed 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -37,6 +37,7 @@ namespace OC\Files\Cache; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use Doctrine\DBAL\Driver\Statement; use OCP\Files\Cache\ICache; @@ -268,12 +269,19 @@ public function insert($file, array $data) { return trim($item, "`"); }, $queryParts); $values = array_combine($queryParts, $params); - if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [ - 'storage', - 'path_hash', - ]) - ) { - return (int)$this->connection->lastInsertId('*PREFIX*filecache'); + try { + if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [ + 'storage', + 'path_hash', + ]) + ) { + return (int)$this->connection->lastInsertId('*PREFIX*filecache'); + } + } 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 } // The file was created in the mean time