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