diff options
author | Nick Vatamaniuc <vatamane@apache.org> | 2020-03-13 18:28:51 -0400 |
---|---|---|
committer | Nick Vatamaniuc <nickva@users.noreply.github.com> | 2020-03-16 10:48:34 -0400 |
commit | 0cd9c26b201ff626f794058b94ed06e496f5baa3 (patch) | |
tree | 1d5e968a0fe0b7b03369b88cd6e31f93b56f96eb | |
parent | 0db143a7a86927cd503baf14e8d56ac922590b46 (diff) | |
download | couchdb-0cd9c26b201ff626f794058b94ed06e496f5baa3.tar.gz |
Refactor some of the "tx_too_old" tests
* There was a good amount of duplication between `_db_crud_tests` and
`_changes_fold_tests`, so make a common test utility module so both suites can
use.
* Clean up test names. Previously some were named `tx_too_long` but since the
official FDB error is `transaction_too_old` rename them to match a bit
better.
* `list_dbs_info` implementation queue of 100 futures to parallelize fetching.
So its test was update to create more than 100 dbs. Creating 100 dbs took
about 3 seconds so add a small parallel map (pmap) utility function to help
with that.
-rw-r--r-- | src/fabric/src/fabric2_util.erl | 49 | ||||
-rw-r--r-- | src/fabric/test/fabric2_changes_fold_tests.erl | 103 | ||||
-rw-r--r-- | src/fabric/test/fabric2_db_crud_tests.erl | 112 | ||||
-rw-r--r-- | src/fabric/test/fabric2_test_util.erl | 76 |
4 files changed, 205 insertions, 135 deletions
diff --git a/src/fabric/src/fabric2_util.erl b/src/fabric/src/fabric2_util.erl index a4faf3987..46f9abeef 100644 --- a/src/fabric/src/fabric2_util.erl +++ b/src/fabric/src/fabric2_util.erl @@ -37,7 +37,10 @@ from_hex/1, uuid/0, - encode_all_doc_key/1 + encode_all_doc_key/1, + + pmap/2, + pmap/3 ]). @@ -298,3 +301,47 @@ encode_all_doc_key(N) when is_number(N) -> <<>>; encode_all_doc_key(B) when is_binary(B) -> B; encode_all_doc_key(L) when is_list(L) -> <<255>>; encode_all_doc_key({O}) when is_list(O) -> <<255>>. + + +pmap(Fun, Args) -> + pmap(Fun, Args, []). + + +pmap(Fun, Args, Opts) -> + Refs = lists:map(fun(Arg) -> + {_, Ref} = spawn_monitor(fun() -> exit(pmap_exec(Fun, Arg)) end), + Ref + end, Args), + Timeout = fabric2_util:get_value(timeout, Opts, 5000), + lists:map(fun(Ref) -> + receive + {'DOWN', Ref, _, _, {'$res', Res}} -> + Res; + {'DOWN', Ref, _, _, {'$err', Tag, Reason, Stack}} -> + erlang:raise(Tag, Reason, Stack) + after Timeout -> + error({pmap_timeout, Timeout}) + end + end, Refs). + + +% OTP_RELEASE is defined in OTP 21+ only +-ifdef(OTP_RELEASE). + +pmap_exec(Fun, Arg) -> + try + {'$res', Fun(Arg)} + catch Tag:Reason:Stack -> + {'$err', Tag, Reason, Stack} + end. + +-else. + +pmap_exec(Fun, Arg) -> + try + {'$res', Fun(Arg)} + catch Tag:Reason -> + {'$err', Tag, Reason, erlang:get_stacktrace()} + end. + +-endif. diff --git a/src/fabric/test/fabric2_changes_fold_tests.erl b/src/fabric/test/fabric2_changes_fold_tests.erl index fddf1802b..8541d973c 100644 --- a/src/fabric/test/fabric2_changes_fold_tests.erl +++ b/src/fabric/test/fabric2_changes_fold_tests.erl @@ -21,9 +21,6 @@ -define(DOC_COUNT, 25). --define(PDICT_ERROR_IN_FOLD_RANGE, '$fabric2_error_in_fold_range'). --define(PDICT_ERROR_IN_USER_FUN, '$fabric2_error_throw_in_user_fun'). - changes_fold_test_() -> { @@ -43,10 +40,10 @@ 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_basic_tx_too_long), - ?TDEF_FE(fold_changes_reverse_tx_too_long), - ?TDEF_FE(fold_changes_tx_too_long_with_single_row_emits), - ?TDEF_FE(fold_changes_since_seq_tx_too_long), + ?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), + ?TDEF_FE(fold_changes_since_seq_tx_too_old), ?TDEF_FE(fold_changes_not_progressing) ] } @@ -66,10 +63,7 @@ teardown_all(Ctx) -> setup() -> - meck:expect(erlfdb, fold_range, fun(Tx, Start, End, Callback, Acc, Opts) -> - maybe_tx_too_long(?PDICT_ERROR_IN_FOLD_RANGE), - meck:passthrough([Tx, Start, End, Callback, Acc, Opts]) - end), + fabric2_test_util:tx_too_old_mock_erlfdb(), {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]), Rows = lists:map(fun(Val) -> DocId = fabric2_util:uuid(), @@ -90,7 +84,7 @@ setup() -> cleanup({Db, _DocIdRevs}) -> - reset_error_counts(), + fabric2_test_util:tx_too_old_reset_errors(), ok = fabric2_db:delete(fabric2_db:name(Db), []). @@ -130,131 +124,114 @@ fold_changes_since_seq_rev({Db, DocRows}) -> fold_changes_since_seq_rev({Db, RestRows}). -fold_changes_basic_tx_too_long({Db, DocRows0}) -> +fold_changes_basic_tx_too_old({Db, DocRows0}) -> DocRows = lists:reverse(DocRows0), - tx_too_long_errors(0, 1), + fabric2_test_util:tx_too_old_setup_errors(0, 1), ?assertEqual(DocRows, changes(Db)), - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual(DocRows, changes(Db)), % Blow up in user fun but after emitting one row successfully. - tx_too_long_errors({1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, 0), ?assertEqual(DocRows, changes(Db)), % Blow up before last document - tx_too_long_errors({?DOC_COUNT - 1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({?DOC_COUNT - 1, 1}, 0), ?assertEqual(DocRows, changes(Db)), % Emit one value, then blow up in user function and then blow up twice in % fold_range. But it is not enough to stop the iteration. - tx_too_long_errors({1, 1}, {1, 2}), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, {1, 2}), ?assertEqual(DocRows, changes(Db)). -fold_changes_reverse_tx_too_long({Db, DocRows}) -> +fold_changes_reverse_tx_too_old({Db, DocRows}) -> Opts = [{dir, rev}], - tx_too_long_errors(0, 1), + fabric2_test_util:tx_too_old_setup_errors(0, 1), ?assertEqual([], changes(Db, 0, Opts)), - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual([], changes(Db, 0, Opts)), - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual(DocRows, changes(Db, now, Opts)), - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual(DocRows, changes(Db, now, Opts)), % Blow up in user fun but after emitting one row successfully. - tx_too_long_errors({1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, 0), ?assertEqual(DocRows, changes(Db, now, Opts)), % Blow up before last document - tx_too_long_errors({?DOC_COUNT - 1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({?DOC_COUNT - 1, 1}, 0), ?assertEqual(DocRows, changes(Db, now, Opts)), % Emit value, blow up in user function, and twice in fold_range - tx_too_long_errors({1, 1}, {1, 2}), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, {1, 2}), ?assertEqual(DocRows, changes(Db, now, Opts)). -fold_changes_tx_too_long_with_single_row_emits({Db, DocRows0}) -> +fold_changes_tx_too_old_with_single_row_emits({Db, DocRows0}) -> % This test does a few basic operations while forcing erlfdb range fold to % emit a single row at a time, thus forcing it to use continuations while % also inducing tx errors Opts = [{target_bytes, 1}], DocRows = lists:reverse(DocRows0), - tx_too_long_errors(0, 1), + fabric2_test_util:tx_too_old_setup_errors(0, 1), ?assertEqual(DocRows, changes(Db, 0, Opts)), - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual(DocRows, changes(Db, 0, Opts)), % Blow up in user fun but after emitting one row successfully. - tx_too_long_errors({1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, 0), ?assertEqual(DocRows, changes(Db, 0, Opts)), % Blow up before last document - tx_too_long_errors({?DOC_COUNT - 1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({?DOC_COUNT - 1, 1}, 0), ?assertEqual(DocRows, changes(Db, 0, Opts)). -fold_changes_since_seq_tx_too_long({Db, Rows}) -> +fold_changes_since_seq_tx_too_old({Db, Rows}) -> % Blow up after after a successful emit, then twice % in range fold call. Also re-use already existing basic % fold_changes_since_seq test function. - tx_too_long_errors({1, 1}, {1, 2}), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, {1, 2}), fold_changes_since_seq({Db, Rows}). fold_changes_not_progressing({Db, _}) -> % Fail in first fold range call. - tx_too_long_errors(5, 0), + fabric2_test_util:tx_too_old_setup_errors(5, 0), ?assertError(fold_range_not_progressing, changes(Db)), % Fail in first user fun call. - tx_too_long_errors(0, 5), + fabric2_test_util:tx_too_old_setup_errors(0, 5), ?assertError(fold_range_not_progressing, changes(Db)), % Blow up in last user fun call - tx_too_long_errors({?DOC_COUNT - 1, 5}, 0), + fabric2_test_util:tx_too_old_setup_errors({?DOC_COUNT - 1, 5}, 0), ?assertError(fold_range_not_progressing, changes(Db)), % Blow up in user function after one success. - tx_too_long_errors({1, 5}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 5}, 0), ?assertError(fold_range_not_progressing, changes(Db)), % Emit value, blow up in user function, then keep blowing up in fold_range. - tx_too_long_errors({1, 1}, {1, 4}), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, {1, 4}), ?assertError(fold_range_not_progressing, changes(Db)). fold_fun(#{} = Change, Acc) -> - maybe_tx_too_long(?PDICT_ERROR_IN_USER_FUN), + fabric2_test_util:tx_too_old_raise_in_user_fun(), {ok, [Change | Acc]}. -tx_too_long_errors(UserFunCount, FoldErrors) when is_integer(UserFunCount) -> - tx_too_long_errors({0, UserFunCount}, FoldErrors); - -tx_too_long_errors(UserFunErrors, FoldCount) when is_integer(FoldCount) -> - tx_too_long_errors(UserFunErrors, {0, FoldCount}); - -tx_too_long_errors({UserFunSkip, UserFunCount}, {FoldSkip, FoldCount}) -> - reset_error_counts(), - put(?PDICT_ERROR_IN_USER_FUN, {UserFunSkip, UserFunCount}), - put(?PDICT_ERROR_IN_FOLD_RANGE, {FoldSkip, FoldCount}). - - -reset_error_counts() -> - erase(?PDICT_ERROR_IN_FOLD_RANGE), - erase(?PDICT_ERROR_IN_USER_FUN). - - changes(Db) -> changes(Db, 0, []). @@ -262,17 +239,3 @@ changes(Db) -> changes(Db, Since, Opts) -> {ok, Rows} = fabric2_db:fold_changes(Db, Since, fun fold_fun/2, [], Opts), Rows. - - -maybe_tx_too_long(Key) -> - case get(Key) of - {Skip, Count} when is_integer(Skip), Skip > 0 -> - put(Key, {Skip - 1, Count}); - {0, Count} when is_integer(Count), Count > 0 -> - put(Key, {0, Count - 1}), - error({erlfdb_error, 1007}); - {0, 0} -> - ok; - undefined -> - ok - end. diff --git a/src/fabric/test/fabric2_db_crud_tests.erl b/src/fabric/test/fabric2_db_crud_tests.erl index 6323a22bd..c0a65ebd8 100644 --- a/src/fabric/test/fabric2_db_crud_tests.erl +++ b/src/fabric/test/fabric2_db_crud_tests.erl @@ -18,10 +18,6 @@ -include("fabric2_test.hrl"). --define(PDICT_ERROR_IN_FOLD_RANGE, '$fabric2_error_in_fold_range'). --define(PDICT_ERROR_IN_USER_FUN, '$fabric2_error_throw_in_user_fun'). - - crud_test_() -> { "Test database CRUD operations", @@ -42,8 +38,8 @@ crud_test_() -> ?TDEF_FE(list_dbs_user_fun_partial), ?TDEF_FE(list_dbs_info), ?TDEF_FE(list_dbs_info_partial), - ?TDEF_FE(list_dbs_tx_too_long), - ?TDEF_FE(list_dbs_info_tx_too_long) + ?TDEF_FE(list_dbs_tx_too_old), + ?TDEF_FE(list_dbs_info_tx_too_old) ] } } @@ -62,15 +58,11 @@ teardown_all(Ctx) -> setup() -> - meck:expect(erlfdb, fold_range, fun(Tx, Start, End, Callback, Acc, Opts) -> - maybe_tx_too_long(?PDICT_ERROR_IN_FOLD_RANGE), - meck:passthrough([Tx, Start, End, Callback, Acc, Opts]) - end), - ok. + fabric2_test_util:tx_too_old_mock_erlfdb(). cleanup(_) -> - reset_error_counts(). + fabric2_test_util:tx_too_old_reset_errors(). create_db(_) -> @@ -166,14 +158,14 @@ list_dbs_info_partial(_) -> ?assertEqual([{meta, []}], UserAcc). -list_dbs_tx_too_long(_) -> +list_dbs_tx_too_old(_) -> DbName1 = ?tempdb(), DbName2 = ?tempdb(), ?assertMatch({ok, _}, fabric2_db:create(DbName1, [])), ?assertMatch({ok, _}, fabric2_db:create(DbName2, [])), UserFun = fun(Row, Acc) -> - maybe_tx_too_long(?PDICT_ERROR_IN_USER_FUN), + fabric2_test_util:tx_too_old_raise_in_user_fun(), {ok, [Row, Acc]} end, @@ -181,64 +173,87 @@ list_dbs_tx_too_long(_) -> Dbs = fabric2_db:list_dbs(UserFun, [], []), % Blow up in fold range - tx_too_long_errors(0, 1), + fabric2_test_util:tx_too_old_setup_errors(0, 1), ?assertEqual(Dbs, fabric2_db:list_dbs(UserFun, [], [])), % Blow up in fold_range after emitting one row - tx_too_long_errors(0, {1, 1}), + fabric2_test_util:tx_too_old_setup_errors(0, {1, 1}), ?assertEqual(Dbs, fabric2_db:list_dbs(UserFun, [], [])), % Blow up in user fun - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(2, 2), ?assertEqual(Dbs, fabric2_db:list_dbs(UserFun, [], [])), % Blow up in user fun after emitting one row - tx_too_long_errors({1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, 0), ?assertEqual(Dbs, fabric2_db:list_dbs(UserFun, [], [])), % Blow up in in user fun and fold range - tx_too_long_errors(1, {1, 1}), + fabric2_test_util:tx_too_old_setup_errors(1, {1, 1}), ?assertEqual(Dbs, fabric2_db:list_dbs(UserFun, [], [])), ok = fabric2_db:delete(DbName1, []), ok = fabric2_db:delete(DbName2, []). -list_dbs_info_tx_too_long(_) -> - DbName1 = ?tempdb(), - DbName2 = ?tempdb(), - ?assertMatch({ok, _}, fabric2_db:create(DbName1, [])), - ?assertMatch({ok, _}, fabric2_db:create(DbName2, [])), +list_dbs_info_tx_too_old(_) -> + % list_dbs_info uses a queue of 100 futures to fetch db infos in parallel + % so create more than 100 dbs so make sure we have 100+ dbs in our test + + DbCount = 101, + DbNames = fabric2_util:pmap(fun(_) -> + DbName = ?tempdb(), + ?assertMatch({ok, _}, fabric2_db:create(DbName, [])), + DbName + end, lists:seq(1, DbCount)), UserFun = fun(Row, Acc) -> - maybe_tx_too_long(?PDICT_ERROR_IN_USER_FUN), + fabric2_test_util:tx_too_old_raise_in_user_fun(), {ok, [Row, Acc]} end, + % This is the expected return with no tx timeouts {ok, DbInfos} = fabric2_db:list_dbs_info(UserFun, [], []), - % Blow up in fold range - tx_too_long_errors(0, 1), + % Blow up in fold range on the first call + fabric2_test_util:tx_too_old_setup_errors(0, 1), ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), % Blow up in fold_range after emitting one row - tx_too_long_errors(0, {1, 1}), + fabric2_test_util:tx_too_old_setup_errors(0, {1, 1}), + ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), + + % Blow up in fold_range after emitting 99 rows + fabric2_test_util:tx_too_old_setup_errors(0, {DbCount - 2, 1}), + ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), + + % Blow up in fold_range after emitting 100 rows + fabric2_test_util:tx_too_old_setup_errors(0, {DbCount - 1, 1}), ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), % Blow up in user fun - tx_too_long_errors(1, 0), + fabric2_test_util:tx_too_old_setup_errors(1, 0), ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), % Blow up in user fun after emitting one row - tx_too_long_errors({1, 1}, 0), + fabric2_test_util:tx_too_old_setup_errors({1, 1}, 0), + ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), + + % Blow up in user fun after emitting 99 rows + fabric2_test_util:tx_too_old_setup_errors({DbCount - 2, 1}, 0), + ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), + + % Blow up in user fun after emitting 100 rows + fabric2_test_util:tx_too_old_setup_errors({DbCount - 1, 1}, 0), ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), % Blow up in in user fun and fold range - tx_too_long_errors(1, {1, 1}), + fabric2_test_util:tx_too_old_setup_errors(1, {1, 1}), ?assertEqual({ok, DbInfos}, fabric2_db:list_dbs_info(UserFun, [], [])), - ok = fabric2_db:delete(DbName1, []), - ok = fabric2_db:delete(DbName2, []). + fabric2_util:pmap(fun(DbName) -> + ?assertEqual(ok, fabric2_db:delete(DbName, [])) + end, DbNames). is_db_info_member(_, []) -> @@ -251,34 +266,3 @@ is_db_info_member(DbName, [DbInfo | RestInfos]) -> _E -> is_db_info_member(DbName, RestInfos) end. - - -tx_too_long_errors(UserFunCount, FoldErrors) when is_integer(UserFunCount) -> - tx_too_long_errors({0, UserFunCount}, FoldErrors); - -tx_too_long_errors(UserFunErrors, FoldCount) when is_integer(FoldCount) -> - tx_too_long_errors(UserFunErrors, {0, FoldCount}); - -tx_too_long_errors({UserFunSkip, UserFunCount}, {FoldSkip, FoldCount}) -> - reset_error_counts(), - put(?PDICT_ERROR_IN_USER_FUN, {UserFunSkip, UserFunCount}), - put(?PDICT_ERROR_IN_FOLD_RANGE, {FoldSkip, FoldCount}). - - -reset_error_counts() -> - erase(?PDICT_ERROR_IN_FOLD_RANGE), - erase(?PDICT_ERROR_IN_USER_FUN). - - -maybe_tx_too_long(Key) -> - case get(Key) of - {Skip, Count} when is_integer(Skip), Skip > 0 -> - put(Key, {Skip - 1, Count}); - {0, Count} when is_integer(Count), Count > 0 -> - put(Key, {0, Count - 1}), - error({erlfdb_error, 1007}); - {0, 0} -> - ok; - undefined -> - ok - end. diff --git a/src/fabric/test/fabric2_test_util.erl b/src/fabric/test/fabric2_test_util.erl new file mode 100644 index 000000000..acbe252b1 --- /dev/null +++ b/src/fabric/test/fabric2_test_util.erl @@ -0,0 +1,76 @@ +% Licensed under the Apache License, Version 2.0 (the "License"); you may not +% use this file except in compliance with the License. You may obtain a copy of +% the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +% License for the specific language governing permissions and limitations under +% the License. + +-module(fabric2_test_util). + + +-export([ + tx_too_old_mock_erlfdb/0, + tx_too_old_setup_errors/2, + tx_too_old_reset_errors/0, + tx_too_old_raise_in_user_fun/0 +]). + + +-define(PDICT_ERROR_IN_FOLD_RANGE, '$fabric2_error_in_fold_range'). +-define(PDICT_ERROR_IN_USER_FUN, '$fabric2_error_throw_in_user_fun'). + + +% Set of function to test scenarios where the FDB throws transaction_too_long +% (1007) errors. The general pattern is to call tx_too_old_mock_erlfdb() in +% setup. Then, before tests call tx_too_old_setup_errors(UserErrs, FoldErrs) +% which will set how and when the error will be thrown. + +tx_too_old_mock_erlfdb() -> + meck:expect(erlfdb, fold_range, fun(Tx, Start, End, Callback, Acc, Opts) -> + MockFun = fun(Row, InnerAcc) -> + maybe_tx_too_old(?PDICT_ERROR_IN_FOLD_RANGE), + Callback(Row, InnerAcc) + end, + meck:passthrough([Tx, Start, End, MockFun, Acc, Opts]) + end). + + +tx_too_old_setup_errors(UserCnt, FoldErrs) when is_integer(UserCnt) -> + tx_too_old_setup_errors({0, UserCnt}, FoldErrs); + +tx_too_old_setup_errors(UserErrs, FoldCnt) when is_integer(FoldCnt) -> + tx_too_old_setup_errors(UserErrs, {0, FoldCnt}); + +tx_too_old_setup_errors({UserSkip, UserCnt}, {FoldSkip, FoldCnt}) -> + put(?PDICT_ERROR_IN_USER_FUN, {UserSkip, UserCnt}), + put(?PDICT_ERROR_IN_FOLD_RANGE, {FoldSkip, FoldCnt}). + + +tx_too_old_reset_errors() -> + erase(?PDICT_ERROR_IN_FOLD_RANGE), + erase(?PDICT_ERROR_IN_USER_FUN). + + +tx_too_old_raise_in_user_fun() -> + maybe_tx_too_old(?PDICT_ERROR_IN_USER_FUN). + + +% Private functions + +maybe_tx_too_old(Key) -> + case get(Key) of + {Skip, Count} when is_integer(Skip), Skip > 0 -> + put(Key, {Skip - 1, Count}); + {0, Count} when is_integer(Count), Count > 0 -> + put(Key, {0, Count - 1}), + error({erlfdb_error, 1007}); + {0, 0} -> + ok; + undefined -> + ok + end. |