To avoid race conditions, I need to use select for update functionality while querying database so that it locks the row until the end of the transaction. Since select_for_update query is not present in Django 1.3, I have done a workaround it by using a class method which returns query sets by doing a raw sql query.
#models.py class AccountDetails(models.Model): user = models.OneToOneField(User) amount = models.IntegerField(max_length=15,null=True,blank=True) @classmethod def get_locked_for_update(cls,userid): accounts = cls.objects.raw("SELECT * FROM b2b_accountdetails WHERE user_id ="+str(userid)+" FOR UPDATE") return accounts[0] This is how it's used in views.
account = AccountDetails.get_locked_for_update(userid) account.amount = account.amount - fare account.save() On the last line I'm getting this error: OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')
In dbshell, after running the save() line:
mysql> SHOW FULL PROCESSLIST; +-----+------+-----------+-----------+---------+------+----------+-----------------------------------------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+-----------+---------+------+----------+-------------------------- ---------------------------------------------------------------------------+ | 51 | root | localhost | dbname | Query | 0 | NULL | SHOW FULL PROCESSLIST | | 767 | root | localhost | dbname | Sleep | 59 | | NULL | | 768 | root | localhost | dbname | Query | 49 | Updating | UPDATE `b2b_accountdetails` SET `user_id` = 1, `amount` = 68906 WHERE `appname_accountdetails`.`id` = 1 | +-----+------+-----------+-----------+---------+------+----------+-------------------------- ---------------------------------------------------------------------------+ According to my understanding the lock should be released on the first data-altering query like update, delete etc.
But the save() statement is getting blocked and is kept on wait. Any idea why this is happening? What I think is when I'm calling account.save() it's not picking up the previous transaction started by select for update query.
Am I missing something obvious? Kindly help.