Skip to content

deadlock retry fails to save record that raises a deadlock in after hooks #10

@mdimas

Description

@mdimas

If a deadlock occurs in an after commit hook for a record deadlock_retry will retry the commit but issues an "UPDATE" instead of an "INSERT" statement, resulting in the record never being saved even though calling #save returns true. This is easily demonstrated with the following code:

require 'deadlock_retry'

class MyTest < ActiveRecord::Base

  after_create :deadlock_error
  after_commit :puts_after_commit, :on => :create

  def deadlock_error
    unless @errored
      @errored = true
      raise ActiveRecord::StatementInvalid.new("Deadlock found when trying to get lock")
    end
    return
  end

  def puts_after_commit
    puts "After Commit"
  end
end

my_test = MyTest.new(:name => "Testing")

if my_test.save
  MyTest.find(my_test.id)
end

This will have the following output:

> my_test = MyTest.new(:name => "Testing")
=> #<MyTest id: nil, name: "Testing"> 
> if my_test.save
?>     MyTest.find(my_test.id)
?>   end

After Commit
ActiveRecord::RecordNotFound: Couldn't find MyTest with ID=1
from /Users/dimas/.rvm/gems/ree-1.8.7-2011.03/gems/activerecord-3.0.11/lib/active_record/relation/finder_methods.rb:306:in `find_one'
from /Users/dimas/.rvm/gems/ree-1.8.7-2011.03/gems/activerecord-3.0.11/lib/active_record/relation/finder_methods.rb:291:in `find_with_ids'
from /Users/dimas/.rvm/gems/ree-1.8.7-2011.03/gems/activerecord-3.0.11/lib/active_record/relation/finder_methods.rb:107:in `find'
from /Users/dimas/.rvm/gems/ree-1.8.7-2011.03/gems/activerecord-3.0.11/lib/active_record/base.rb:444:in `__send__'
from /Users/dimas/.rvm/gems/ree-1.8.7-2011.03/gems/activerecord-3.0.11/lib/active_record/base.rb:444:in `find'
from (irb):22

And if you look at the log you'll see:

SQL (0.1ms)  BEGIN
SQL (1.9ms)  describe `my_tests`
AREL (0.2ms)  INSERT INTO `my_tests` (`name`) VALUES ('Testing')
SQL (0.5ms)  ROLLBACK
Deadlock detected on retry 1, restarting transaction
SQL (3.3ms)  BEGIN
AREL (0.3ms)  UPDATE `my_tests` SET `name` = 'Testing' WHERE `my_tests`.`id` = 1
SQL (0.1ms)  COMMIT
MyTest Load (0.2ms)  SELECT `my_tests`.* FROM `my_tests` WHERE `my_tests`.`id` = 1 LIMIT 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions