Home /
Lost in Transaction
Lost in Transaction
November 4, 2010
rails3
callbacks
transactions
I am working on an issue tracking system at work. I built in an API so other systems can create events, but I wanted to make sure to minimize duplicate events, and opted to just add a comment in case the same issue was opened twice (or more.)
I started with this test
context "don't allow duplicate issues" do
setup do
@issue = Issue . create ( :name => "something" , :start_at => "1/1/2010" )
end
should "not create a duplicate issue" do
assert_no_difference ( 'Issue.count' ) do
another_issue = Issue . create ( :name => "something" , :start_at => "1/1/2010" )
end
end
should "create a new issue if the duplicate issue is closed" do
@issue . update_attributes ( :resolution => "something" )
assert_difference ( 'Issue.count' ) do
another_issue = Issue . create ( :name => "something" , :start_at => "1/1/2010" )
end
end
should "add a comment on existing issue" do
assert_difference ( '@issue.comments.count' ) do
another_issue = Issue . create ( :name => "something" , :start_at => "1/1/2010" )
end
end
end
Then wrote this code:
class Issue < ActiveRecord :: Base
has_many :comments
validate :check_for_open_duplicate , :on => :create
scope :open , where ( :resolution => nil )
protected
def check_for_open_duplicate
open_issue = Issue . open . where ( :name => name , :description => description ). first
if open_issue
open_issue . comments . create ( :body => "Duplicate attempted to open" )
errors . add ( :duplicate , "Duplicate Issue" )
end
end
end
But it kept failing, which was weird to me, because it seemed right.. it wasn't until I tried it from the development environment and checked out the logs that I saw what was happening..
SQL (0.1ms) BEGIN
Issue Load (0.1ms) SELECT `issues`.* FROM `issues` WHERE (`issues`.`resolution` IS NULL) AND (`issues`.`description` = '') AND (`issues`.`name` = 'something') ORDER BY start_at DESC LIMIT 1
SQL (0.2ms) SELECT COUNT(*) AS count_id FROM `comments`
SQL (18.6ms) INSERT INTO `comments` (`body`, `commentable_id`, `commentable_type`, `created_at`, `updated_at`, `user_id`) VALUES ('Duplicate attempted to open', 3370, 'Issue', '2010-11-03 14:36:12', '2010-11-03 14:36:12', 1)
SQL (47.5ms) ROLLBACK
The whole request was being wrapped in a transaction and being rolled back.. A little google searching later, I found
after_rollback , and wrote a new callback
class Issue < ActiveRecord :: Base
attr_accessor :duplicate_issue
has_many :comments
validate :check_for_open_duplicate , :on => :create
after_rollback :add_duplicate_comment
scope :open , where ( :resolution => nil )
protected
def check_for_open_duplicate
open_issue = Issue . open . where ( :name => name , :description => description ). first
if open_issue
@duplicate_issue = open_issue
errors . add ( :duplicate , "Duplicate Issue" )
end
end
def add_duplicate_comment
@duplicate_issue . comments . create ( :body => "Duplicate attempted to open" ) if @duplicate_issue
end
end
And now everything is working as intended. So if you run into a similar situation, I hope that this will help out.