How to lock a whole table in symfony2 with doctrine2?

有些话、适合烂在心里 提交于 2019-12-21 16:51:16

问题


I need to lock a whole table (not a single row) with doctrine, I would like to do this without native queries if possible.

The documentation for pessimistic locking only describes how to lock specific entities through these methods:

  • EntityManager#find
  • EntityManager#lock
  • Query#setLockMode

I have a transaction that needs to insert a row whose values depend on the values of the rest of the rows in the table, so I need to prevent two transactions performing at the same time on that table.

I'm using explicit transaction demarcation, which is supposed to work well with locking (according to the documentation above).

NOTE: Optimistic locking is not good enough in this case, I can't afford retrying the transaction. Besides the query is not supposed to be slow so performance isn't an issue.

EDIT: I'll give an example. Imagine you want to hand build an auto_increment, and you have to select max() from the table to get the previous result in order to insert the next one. You have to make sure that no two transactions try to insert the same value in case they select max() at the same time.

I'm looking for a general solution to this problem when auto_increment is not good, for example with strings, or multiple columns, hashes or whatever calculation you have to make on the previous row set.

Locking is a solid solution and, unlike optimistic locking, you don't have to retry on errors.

So, is there any way to use table locking in doctrine?


回答1:


Possibly duplicated by Doctrine2 ORM select for update

Here's some related code:

try {
    $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);

    // do the work

    $em->flush();
} catch(OptimisticLockException $e) { 
    echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
}

The LockMode::OPTIMISTIC parameter might provide what you need.




回答2:


Following advice so far, I tried this:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

// calculate $new_number...

// persist $new_number on table_name...
$table_name->setCalculatedNumber($new_number);
$em->persist($table_name);
$em->flush();

$em->getConnection()->exec('UNLOCK TABLES;');

I tested it with JMeter, and the locking wasn't working with a heavy load (16 requests/sec). Tracing showed that other instances got the lock before it had been explicitly given up. The problem (as suggested by Jens) was that flush() implicitly begins with a START TRANSACTION, which drops the table lock. Using a native Update fixed the problem for me:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

// calculate $new_number...

// persist $new_number on table_name...
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");    

$em->getConnection()->exec('UNLOCK TABLES;');
$em->refresh($table_name);

The trailing refresh() was needed to make the calculated number available in subsequent query results




回答3:


From looking at the documentation in Doctrine 2.x I don't think there is a supported way of locking the whole table. You could of course try locking all records via Doctrine individually, but this would be cumbersome and isn't really a good idea.

Instead, I would use the Doctrine entity manager to execute raw SQL on the database...

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

and then after you're done with your update...

$em->getConnection()->exec('UNLOCK TABLES;');

EDIT:

From MySQL documentation on table locks...

  • The session that holds the lock can read and write the table.

  • Only the session that holds the lock can access the table. No other session can access it until the lock is released.

  • Lock requests for the table by other sessions block while the WRITE lock is held.

I think the second point here is key, only your session can read/write to that table.



来源:https://stackoverflow.com/questions/11256169/how-to-lock-a-whole-table-in-symfony2-with-doctrine2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!