Module: ActiveRecord::Locking

Defined in:
lib/active_record/locking.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.append_features(base) ⇒ Object

Active Records support optimistic locking if the field lock_version is present. Each update to the record increments the lock_version column and the locking facilities ensure that records instantiated twice will let the last one saved raise a StaleObjectError if the first was also updated. Example:

p1 = Person.find(1)
p2 = Person.find(1)

p1.first_name = "Michael"
p1.save

p2.first_name = "should fail"
p2.save # Raises a ActiveRecord::StaleObjectError

You’re then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict.

You must ensure that your database schema defaults the lock_version column to 0.

This behavior can be turned off by setting ActiveRecord::Base.lock_optimistically = false.



22
23
24
25
26
27
28
# File 'lib/active_record/locking.rb', line 22

def self.append_features(base)
  super
  base.class_eval do
    alias_method :update_without_lock, :update
    alias_method :update, :update_with_lock
  end
end

Instance Method Details

#update_with_lockObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/active_record/locking.rb', line 30

def update_with_lock
  if locking_enabled?
    previous_value    = self.lock_version
    self.lock_version = previous_value + 1
    
    affected_rows = connection.update(
      "UPDATE #{self.class.table_name} "+
      "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
      "WHERE #{self.class.primary_key} = #{quote(id)} AND lock_version = #{quote(previous_value)}",
      "#{self.class.name} Update with optimistic locking"
    )
    
    raise(ActiveRecord::StaleObjectError, "Attempted to update a stale object") unless affected_rows == 1
  else
    update_without_lock
  end
end