Added an update_doc method to database to handle conflicts during atomic updates.

Also added appropriate specs

Signed-off-by: Marcos Tapajós <tapajos@gmail.com>
This commit is contained in:
Pierre Larochelle 2009-12-18 00:03:05 -05:00 committed by Marcos Tapajós
parent 0c1da919a0
commit cc9ed83cd3
2 changed files with 74 additions and 0 deletions

View file

@ -249,6 +249,33 @@ module CouchRest
CouchRest.copy "#{@root}/#{slug}", destination
end
# Updates the given doc by yielding the current state of the doc
# and trying to update update_limit times. Returns the new doc
# if the doc was successfully updated without hitting the limit
def update_doc(doc_id, params = {}, update_limit=10)
resp = {'ok' => false}
new_doc = nil
last_fail = nil
until resp['ok'] or update_limit <= 0
doc = self.get(doc_id, params) # grab the doc
new_doc = yield doc # give it to the caller to be updated
begin
resp = self.save_doc new_doc # try to PUT the updated doc into the db
rescue RestClient::RequestFailed => e
if e.http_code == 409 # Update collision
update_limit -= 1
last_fail = e
else # some other error
raise e
end
end
end
raise last_fail unless resp['ok']
new_doc
end
# Compact the database, removing old document revisions and optimizing space use.
def compact!
CouchRest.post "#{@root}/_compact"

View file

@ -551,6 +551,53 @@ describe CouchRest::Database do
end
describe "UPDATE existing document" do
before :each do
@id = @db.save_doc({
'article' => 'Pete Doherty Kicked Out For Nazi Anthem',
'upvotes' => 10,
'link' => 'http://beatcrave.com/2009-11-30/pete-doherty-kicked-out-for-nazi-anthem/'})['id']
end
it "should work under normal conditions" do
@db.update_doc @id do |doc|
doc['upvotes'] += 1
doc
end
@db.get(@id)['upvotes'].should == 11
end
it "should fail if update_limit is reached" do
lambda do
@db.update_doc @id do |doc|
# modify and save the doc so that a collision happens
conflicting_doc = @db.get @id
conflicting_doc['upvotes'] += 1
@db.save_doc conflicting_doc
# then try saving it through the update
doc['upvotes'] += 1
doc
end
end.should raise_error(RestClient::RequestFailed)
end
it "should not fail if update_limit is not reached" do
limit = 5
lambda do
@db.update_doc @id do |doc|
# same as the last spec except we're only forcing 5 conflicts
if limit > 0
conflicting_doc = @db.get @id
conflicting_doc['upvotes'] += 1
@db.save_doc conflicting_doc
limit -= 1
end
doc['upvotes'] += 1
doc
end
end.should_not raise_error
@db.get(@id)['upvotes'].should == 16
end
end
describe "COPY existing document" do
before :each do
@r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'})