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:
parent
0c1da919a0
commit
cc9ed83cd3
|
@ -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"
|
||||
|
|
|
@ -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'})
|
||||
|
|
Loading…
Reference in a new issue