From: Adam Kocoloski <adam@cloudant.com>
Date: Wed, 15 Jul 2015 17:06:18 -0400
Subject: [PATCH] Preserve bucket ordering during validation
Document buckets are sorted by docid, but the validation code was
reversing the buckets. If multiple clients send concurrent updates for
the same document the broken sorting can result in duplicate documents.
COUCHDB-2735
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index 11ea0fd..2957818 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -517,7 +517,8 @@ prep_and_validate_update(Db, #doc{id=Id,revs={RevStart, Revs}}=Doc,
prep_and_validate_updates(_Db, [], [], _AllowConflict, AccPrepped,
AccFatalErrors) ->
- {AccPrepped, AccFatalErrors};
+ AccPrepped2 = lists:reverse(lists:map(fun lists:reverse/1, AccPrepped)),
+ {AccPrepped2, AccFatalErrors};
prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
AllowConflict, AccPrepped, AccErrors) ->
{PreppedBucket, AccErrors3} = lists:foldl(
@@ -543,7 +544,7 @@ prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
{[], AccErrors}, DocBucket),
prep_and_validate_updates(Db, RestBuckets, RestLookups, AllowConflict,
- [lists:reverse(PreppedBucket) | AccPrepped], AccErrors3);
+ [PreppedBucket | AccPrepped], AccErrors3);
prep_and_validate_updates(Db, [DocBucket|RestBuckets],
[{ok, #full_doc_info{rev_tree=OldRevTree}=OldFullDocInfo}|RestLookups],
AllowConflict, AccPrepped, AccErrors) ->
@@ -579,7 +580,8 @@ update_docs(Db, Docs, Options) ->
prep_and_validate_replicated_updates(_Db, [], [], AccPrepped, AccErrors) ->
Errors2 = [{{Id, {Pos, Rev}}, Error} ||
{#doc{id=Id,revs={Pos,[Rev|_]}}, Error} <- AccErrors],
- {lists:reverse(AccPrepped), lists:reverse(Errors2)};
+ AccPrepped2 = lists:reverse(lists:map(fun lists:reverse/1, AccPrepped)),
+ {AccPrepped2, lists:reverse(Errors2)};
prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldInfo], AccPrepped, AccErrors) ->
case OldInfo of
not_found ->