Created
November 16, 2010 16:38
-
-
Save AndrewO/702039 to your computer and use it in GitHub Desktop.
Demonstration of a race condition in MongoMapper without support for multiple atomic operators.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Company | |
include MongoMapper::Document | |
key :name, String | |
key :bosses, Array | |
key :employees, Array | |
def promote!(employee) | |
add_to_set(:bosses, employee) | |
pull(:employee, employee) | |
end | |
def hassle_employee(employee) | |
yeaaaah if employees.include?(employee) | |
end | |
def yeaaaah | |
sleep 60 * 60 * 8 | |
end | |
end | |
Company.create(:name => "Initech", | |
:bosses => [:lumbergh, :dom], | |
:employees => [:peter, :michael, :samir, :milton]) | |
# :the_bobs (not shown) want to promote :peter | |
# :lumbergh wants to hassle :peter | |
c1 = Company.find(:name => "Initech") | |
c1.promote!(:peter) #1 | |
1 --> bosses.add_to_set(:bosses, :peter) | |
1 ----> db.collection("companies").update({:name => "Initech"}, | |
{"$addToSet" => {:bosses => "peter"}}) | |
# :lumbergh (with a different Company object, since it could be in another request) | |
c2 = Company.find(:name => "Initech") | |
c2.hassle_employee(:peter) #2 | |
2 --> employess.include?(:peter) => true | |
2 --> yeaaaah # Peter goes insane | |
# 2nd step of promote!(:peter) | |
1 --> employees.pull(:employees, :peter) | |
1 ----> db.collection("companies").update({:name => "Initech"}, | |
{"$pull" => {:employees => "peter"}}) | |
class Company | |
# Override old promote! with something that uses atomic updates | |
def promote!(employee) | |
modify do | |
add_to_set(:bosses, employee) | |
pull(:employees, employee) | |
end | |
end | |
end | |
# :lumbergh wants to hassle :peter | |
# :the_bobs (not shown) want to promote :peter | |
c1 = Company.find(:name => "Initech") | |
c1.promote!(:peter) #1 | |
1 --> modify { add_to_set(:bosses, :peter); pull(:employees, :peter)} | |
1 ----> db.collection("companies").update({:name => "Initech"}, | |
{"$addToSet" => {:bosses => "peter"}, "$pull" => {:employees => "peter"}) | |
# :lumbergh (with a different Company object, since it could be in another request) | |
c2 = Company.find(:name => "Initech") | |
c2.hassle_employee(:peter) #2 | |
2 --> employees.include?(:peter) => false # Peter doesn't have to put up with Lumbergh anymore |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See discussion: http://groups.google.com/group/mongomapper/browse_thread/thread/4b48720e5f5aec77