This is an automated email from the ASF dual-hosted git repository.

davisp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 0e91f2f608d4a920b6c22f1fe1eb9e0ea6982d5a
Author: Paul J. Davis <paul.joseph.da...@gmail.com>
AuthorDate: Fri Nov 13 15:13:07 2020 -0600

    Allow specifying an end_key for fold_changes
    
    This is useful so that read conflicts on the changes feed will
    eventually be resolved. Without an end key specified a reader could end
    up in an infinite conflict retry loop if there are clients updating
    documents in the database.
---
 src/fabric/src/fabric2_db.erl                  | 18 +++++++++++-------
 src/fabric/test/fabric2_changes_fold_tests.erl | 11 +++++++++++
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index b3e510b..a310470 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -1093,14 +1093,18 @@ fold_changes(Db, SinceSeq, UserFun, UserAcc, Options) ->
             end,
 
             StartKey = get_since_seq(TxDb, Dir, SinceSeq),
-            EndKey = case Dir of
-                rev -> fabric2_util:seq_zero_vs();
-                _ -> fabric2_util:seq_max_vs()
+            EndKey = case fabric2_util:get_value(end_key, Options) of
+                undefined when Dir == rev ->
+                    fabric2_util:seq_zero_vs();
+                undefined ->
+                    fabric2_util:seq_max_vs();
+                EK when is_binary(EK) ->
+                    fabric2_fdb:seq_to_vs(EK);
+                EK when is_tuple(EK), element(1, EK) == versionstamp ->
+                    EK
             end,
-            FoldOpts = [
-                {start_key, StartKey},
-                {end_key, EndKey}
-            ] ++ RestartTx ++ Options,
+            BaseOpts = [{start_key, StartKey}] ++ RestartTx ++ Options,
+            FoldOpts = lists:keystore(end_key, 1, BaseOpts, {end_key, EndKey}),
 
             {ok, fabric2_fdb:fold_range(TxDb, Prefix, fun({K, V}, Acc) ->
                 {SeqVS} = erlfdb_tuple:unpack(K, Prefix),
diff --git a/src/fabric/test/fabric2_changes_fold_tests.erl 
b/src/fabric/test/fabric2_changes_fold_tests.erl
index 8541d97..fa79f25 100644
--- a/src/fabric/test/fabric2_changes_fold_tests.erl
+++ b/src/fabric/test/fabric2_changes_fold_tests.erl
@@ -40,6 +40,7 @@ changes_fold_test_() ->
                     ?TDEF_FE(fold_changes_basic_rev),
                     ?TDEF_FE(fold_changes_since_now_rev),
                     ?TDEF_FE(fold_changes_since_seq_rev),
+                    ?TDEF_FE(fold_changes_with_end_key),
                     ?TDEF_FE(fold_changes_basic_tx_too_old),
                     ?TDEF_FE(fold_changes_reverse_tx_too_old),
                     ?TDEF_FE(fold_changes_tx_too_old_with_single_row_emits),
@@ -124,6 +125,16 @@ fold_changes_since_seq_rev({Db, DocRows}) ->
     fold_changes_since_seq_rev({Db, RestRows}).
 
 
+fold_changes_with_end_key({Db, DocRows}) ->
+    lists:foldl(fun(DocRow, Acc) ->
+        EndSeq = maps:get(sequence, DocRow),
+        Changes = changes(Db, 0, [{end_key, EndSeq}]),
+        NewAcc = [DocRow | Acc],
+        ?assertEqual(Changes, NewAcc),
+        NewAcc
+    end, [], DocRows).
+
+
 fold_changes_basic_tx_too_old({Db, DocRows0}) ->
     DocRows = lists:reverse(DocRows0),
 

Reply via email to