-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix for race condition when multiple processes try to add the same tag #499
Changes from 1 commit
324f523
25362b0
b85df7d
ce60248
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -246,7 +246,7 @@ | |
unless ActsAsTaggableOn::Tag.using_sqlite? | ||
it "should not care about case for unicode names" do | ||
ActsAsTaggableOn.strict_case_match = false | ||
|
||
anya = TaggableModel.create(:name => "Anya", :tag_list => "ПРИВЕТ") | ||
igor = TaggableModel.create(:name => "Igor", :tag_list => "привет") | ||
katia = TaggableModel.create(:name => "Katia", :tag_list => "ПРИВЕТ") | ||
|
@@ -269,7 +269,7 @@ | |
TaggableModel.tagged_with("中国的").to_a.size.should == 1 | ||
TaggableModel.tagged_with("العربية").to_a.size.should == 1 | ||
TaggableModel.tagged_with("✏").to_a.size.should == 1 | ||
end | ||
end | ||
|
||
it "should be able to get tag counts on model as a whole" do | ||
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css") | ||
|
@@ -482,13 +482,43 @@ | |
TaggableModel.tagged_with([]).should == [] | ||
end | ||
|
||
it "should not create duplicate taggings" do | ||
bob = TaggableModel.create(:name => "Bob") | ||
lambda { | ||
bob.tag_list << "happier" | ||
bob.tag_list << "happier" | ||
bob.save | ||
}.should change(ActsAsTaggableOn::Tagging, :count).by(1) | ||
describe "Duplicates" do | ||
it "should not create duplicate taggings" do | ||
bob = TaggableModel.create(:name => "Bob") | ||
lambda { | ||
bob.tag_list << "happier" | ||
bob.tag_list << "happier" | ||
bob.save | ||
}.should change(ActsAsTaggableOn::Tagging, :count).by(1) | ||
end | ||
|
||
if ActsAsTaggableOn::Tag.supports_concurrency? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ActsAsTaggableOn::Utils There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Has to be a module_function or mixed in to be able to call using ActsAsTaggableOn::Utils. Other usage of the functions in ActsAsTaggableOn::Utils reference ActsAsTaggableOn::Tag. I can make the functions in ActsAsTaggableOn::Utils module_functions and update the usage, or something else if you have another preference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The functions are already there : https://github.com/LessonPlanet/acts-as-taggable-on/blob/handle-race-condition-adding-tags/lib/acts_as_taggable_on/utils.rb There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could be missing something. I see all instance methods in the Utils module. From the Module docs, "module methods may be called without creating an encapsulating object, while instance methods may not". There are a few ways to go.
I am happy to do any of the above. Let me know which works for you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think i didn't explain well my point. ActsAsTaggableOn::Tag extends ActsAsTaggableOn::Utils. So i think it better to do This is just a suggestion . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand and agree. I don't think I explained myself very well. When I make this change and run specs I get the following error: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. |
||
it "should not duplicate tags added on different threads" do | ||
thread_count = 4 | ||
barrier = Barrier.new thread_count | ||
|
||
results = [] | ||
expect { | ||
thread_count.times.map do |idx| | ||
Thread.start do | ||
connor = TaggableModel.first_or_create(:name => "Connor") | ||
connor.tag_list = "There, can, be, only, one" | ||
barrier.wait | ||
connor.save | ||
results[idx] = connor.errors.full_messages | ||
end | ||
end.map(&:join) | ||
}.to change(ActsAsTaggableOn::Tag, :count).by(5) | ||
|
||
errors = results.flatten | ||
if ActsAsTaggableOn::Tag.aborts_on_duplicate? | ||
# only one thread succeeds because the transaction is aborted in this case | ||
errors.should eq Array.new(thread_count - 1, "Error saving tags: 'There' has already been taken") | ||
else | ||
errors.should be_empty | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe "Associations" do | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this should be a context