In my Rails code I need to confirm that an action is allowed only if there is more than 1 of a certain record remaining. For this reason I need to lock updates and then perform a read. My rails code looks like this:
PaymentProfile.transaction do
profiles = PaymentProfile.lock("LOCK IN SHARE MODE").where(user_id: xxx)
if profiles.count > 1
#allow
else
#do not allow
end
end
In theory this works well and does lock the rows correctly. HOWEVER, if another request traverses the same code path opening the transaction removes the lock I took out in the other process, thus defeating the purpose of the lock.
From the MySQL docs:
Beginning a transaction also causes table locks acquired with LOCK TABLES to be released, as though you had executed UNLOCK TABLES. Beginning a transaction does not release a global read lock acquired with FLUSH TABLES WITH READ LOCK.