summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2020-11-13 15:13:07 -0600
committerPaul J. Davis <paul.joseph.davis@gmail.com>2020-11-17 15:53:52 -0600
commit0e91f2f608d4a920b6c22f1fe1eb9e0ea6982d5a (patch)
tree8eca2ee9712ac090bfb23a3e717327dcb7b7397f
parent1b30d0810ac0d985676c90dc1b6532f9fb84284c (diff)
downloadcouchdb-0e91f2f608d4a920b6c22f1fe1eb9e0ea6982d5a.tar.gz
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.
-rw-r--r--src/fabric/src/fabric2_db.erl18
-rw-r--r--src/fabric/test/fabric2_changes_fold_tests.erl11
2 files changed, 22 insertions, 7 deletions
diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index b3e510b2e..a310470ea 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 8541d973c..fa79f25f0 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),