1

I have a field on a User table that holds the account balance for the user. Users can perform a lot of actions with my service that will result in rapid changes to their balance.

I'm trying to use mysql's serializable isolation level to make sure that multiple user actions will not update the value incorrectly. (Action A and action B simultaneously want to deduct 1 dollar from the balance.) However, I'm getting a lot of deadlock errors.

How do I do this correctly without getting all these deadlocks, and still keeping the balance field up to date?

simple schema: user has an id and a balance.

im using doctrine, so i'm doing something like the following:

$con->beginTransaction();
$tx = $con->transaction;
$tx->setIsolation('SERIALIZABLE');

$user = UserTable::getInstance()->find($userId);
$user->setBalance($user->getBalance() + $change);
$user->save();
$con->commit();
  • 2
    It would be easier to address the question if you provided the schema as well as access patterns for selects, updates, what all is done transactionally. – atxdba Jan 11 '12 at 23:37

1 Answers1

0

First trying to use serializable isolation level on your transaction is a good idea. It means you know at least a minimum what a transation is, and that the isolation level is one of the biggest problem.

Note that serializable is not really a true seriability. More on that on this previous answer, when you'll have some time to read it :-).

But the most important part is that you should consider that having automatic rollbacks on your transaction because of failed serialibility is a normal fact, and that the right thing to do is building your application so that transactions could fail and should be replayed.

One simple solution, and for accounting things I like this simple solution as we can predict all the facts, no suprises, so, one solution is to perform table locks. This is not a fine and elegant solution, no row levels locks, just simple big table locks (and always in the same order). After that you can do your operation as a single player and then release teh locks. Not multi user concurrency on the rows of the tables, no next-row magical locks fails (see previous link). This will certainly slow down your write operations, but if everybody performs the table locks in the same order you'll only get locks timeouts problems, no deadlocks and no 'unserializable auto-rollback'.

Edit

From your code sample I'm not sure you can set the transaction isolation level after the begin. You should activate query logs on MySQL and seewhat is done, then check that other transactions runned by the CMS are not still in the serializable level.

Community
  • 1
  • 1
regilero
  • 29,806
  • 6
  • 60
  • 99