Datalocking in Rails // 2-7-15
I've been going over Rails 4 guide by Obie Fernandez, and I'm really trying to bring to light things that I've overlooked with ActiveRecord. I frequently have my go-to's, but I really want a thorough understanding of everything there is to offer.
A cool concept I learned today through this book as well as this video was the concept of Data locking.
So let's get into it..
What is Datalocking?
Datalocking is a term to describe techniques that prevent multiple concurrent users of an app from updating + overwriting a record. Matt accesses the venue page and wants to change a detail. Sean does the same. And it's 9:45pm. Say Matt updates the page before Sean. Datalocking will prevent Sean from overwriting the venue page.
There's two types of Datalocking supported by ActiveRecord: optimistic and pessimistic.
Look on the bright side
Optimistic Loading resolve collision if they occur. What I mean by that is that the locking mechanism is occuring provided something triggers it.
class AddLockVersionToVenues < ActiveRecord::Migration
def change
add_column :venues, :lock_version, :integer, default:0
end
end
You add the lock version column to the table you want optimistic loading on. Think of the lock_version property of the version of the record you have. Each time you update the Bada-Bing (Sopranos reference ahem) record, the lock_version will increment.
Back to our favorite buffoons. So let's say two concurrent users loaded up that record. Each of them are on the edit page and want to make an update. But John made it first, the lock_version will increment. Now the record has been updated in the database, with a +1 lock_version
But then where does that leave Sean ? He's clinging onto a Stale object. If he tries to update the Bada Bing , then a ActiveRecord::StaleObjectError shall be raised.
The way to deal with this when using optimistic loading is to have a rescue statement in the controller.
A couple things to note here:
- very little modification has to be made to implement this feature
- update operation is slower since lock_version has to be checked.
- like above, it sucks for the user who spent time inputting all this crap to realize that the Bing was modified by Mike and Sean was making changes.
Glass half empty
Or is it? You don't have to modify your table at all. Here, you're taking advantage of the Database's built-in locking and transaction mechanisms.
Transactions exist so you can sync up multiple database operations in a single 'all or nothing' operation. Whatever is within the transaction block is not visible to the outside, and ALL the changes take effect at the completion of the block.
Jacking this example from railsguides:
Account.transaction do
# select * from accounts where name = 'shugo' limit 1 for update
shugo = Account.where("name = 'shugo'").lock(true).first
yuko = Account.where("name = 'yuko'").lock(true).first
shugo.balance -= 100
shugo.save!
yuko.balance += 100
yuko.save!
end
What anime did they find these names? Anyway, the rows returned by the SQL query (in this case a AR method that's distilled down to it's corresponding query) will be locked. Upon completion of the transaction, the locked state of the record will end.
To note
- super easy to implement
- keep transactions small to avoid extended processing time that where one rails process waiting on another. (rails processes often single threaded)