Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch and ignore UniqueConstraintViolationException in file locking init #12366

Conversation

MorrisJobke
Copy link
Member

@MorrisJobke MorrisJobke commented Nov 9, 2018

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)
 #4 lib/private/DB/Connection.php(251): OC\DB\Adapter->insertIfNotExist('*PREFIX*file_lo...', Array, Array)
 #5 lib/private/Lock/DBLockingProvider.php(118): OC\DB\Connection->insertIfNotExist('*PREFIX*file_lo...', Array, Array)
 #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}

@obel1x Please have a look at this.

* 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>
@MorrisJobke
Copy link
Member Author

MorrisJobke commented Nov 9, 2018

@matt-horwood-mayden @Temtaime @OneOfOne @jirutka Could you test if this fixes the issues that you are seeing in #9305?

@MorrisJobke
Copy link
Member Author

@mxschmitt @martinber Could you check if this fixes the issues you had in #9305?

@kesselb
Copy link
Contributor

kesselb commented Nov 9, 2018

I would prefer insert instead of insertIfNotExists here.

  1. There should be an unique index

    $table->addUniqueIndex(['key'], 'lock_key_index');

  2. https://dev.mysql.com/doc/refman/8.0/en/insert-select.html

The target table of the INSERT statement may appear in the FROM clause of the SELECT part of the query. However, you cannot insert into a table and select from the same table in a subquery. When selecting from and inserting into the same table, MySQL creates an internal temporary table to hold the rows from the SELECT and then inserts those rows into the target table. However, you cannot use INSERT INTO t ... SELECT ... FROM t when t is a TEMPORARY table, because TEMPORARY tables cannot be referred to twice in the same statement.

I guess a temporary table with a few records is not that bad. But for the use case (as far as i understand it) you dont need it?

Maybe I'm missing something but when you catch the UniqueConstraintViolationException anyway this "fake" atomic insert is not required anymore?

@MorrisJobke
Copy link
Member Author

Let's go for #12371, which addresses the first comment of @danielkesselberg and was also the feedback from other developers on IRC.

@MorrisJobke MorrisJobke closed this Nov 9, 2018
@MorrisJobke MorrisJobke deleted the bugfix/noid/catch-unique-constraint-violation-in-file_locks branch November 9, 2018 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3. to review Waiting for reviews bug
Projects
None yet
2 participants