Skip to content

Instantly share code, notes, and snippets.

@AndrewO
Created November 16, 2010 16:38
Show Gist options
  • Save AndrewO/702039 to your computer and use it in GitHub Desktop.
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.
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
@AndrewO
Copy link
Author

AndrewO commented Nov 16, 2010

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment