ActiveRecord Optimistic Locking
Optimistic locking is an alternative to pessimistic locking except that it sort of "builds in" locking mechanisms to an entire table and its corresponding ActiveRecord model. In pessimistic locking, you have to manually call
with_lock on an ActiveRecord model while in optimistic locking, you introduce a column called
lock_version in your database table, which automatically enables optimistic locking.
lock_version column will be incremented every time a change is committed to the record in question. Thus, if there are two processes accessing the same record, and one process makes an update to the record, the second process won't be able to modify the record unless it re-retrieves the newly updated data. This can prevent problems that can come with concurrent access to the same data.
This is pretty much straight from the Rails docs but I can't think of any better way to explain some example usages of optimistic locking, so here goes.
Example 1: Optimistic locking preventing two instances of the same record overriding each other.
p1 = Person.find(1) p2 = Person.find(1) p1.first_name = "Michael" p1.save p2.first_name = "should fail" p2.save # Raises an ActiveRecord::StaleObjectError
Example 2: Optimistic locking preventing deletion of the same record when lock_version is out of date.
p1 = Person.find(1) p2 = Person.find(1) p1.first_name = "Michael" p1.save p2.destroy # Raises an ActiveRecord::StaleObjectError
To add optimistic locking to an ActiveRecord model, add the column
lock_version to your database table with a datatype of
integer. Something like this.
class AddLockingToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :lock_version, :integer, default: 0, null: false end end
lock_version is the default column name that Rails will look for, but you can also customize the column name by overriding an attribute called
locking_column within the ActiveRecord model like this.
class User < ActiveRecord::Base self.locking_column = :custom_locking_column_name end
Personally, I prefer pessimistic locking since I can choose specific instances where I want to lock records. To me, optimistic locking seems akin to the evil (in my mind)
default_scope that override default scoping behaviors in ActiveRecord.
I would say optimistic locking is a toolbox that one can grab for, but only do so when there's a really good reason to do it as it completely overrides out-of-box default ActiveRecord behavior.