blob: 9037f0e23359e83c2b6d038949fb7e167a512275 [file] [log] [blame]
% 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_fdb_tx_retry_tests).
-include_lib("eunit/include/eunit.hrl").
-include("fabric2_test.hrl").
retry_test_() ->
{
setup,
fun setup/0,
fun cleanup/1,
with([
?TDEF(read_only_no_retry),
?TDEF(read_only_commit_unknown_result),
?TDEF(run_on_first_try),
?TDEF(retry_when_commit_conflict),
?TDEF(retry_when_txid_not_found),
?TDEF(no_retry_when_txid_found),
?TDEF(use_last_unknown_result)
])
}.
setup() ->
meck:new(erlfdb),
meck:new(fabric2_txids),
EnvSt =
case application:get_env(fabric, db) of
{ok, Db} -> {ok, Db};
undefined -> undefined
end,
application:set_env(fabric, db, not_a_real_db),
EnvSt.
cleanup(EnvSt) ->
case EnvSt of
{ok, Db} -> application:set_env(fabric, db, Db);
undefined -> application:unset_env(fabric, db)
end,
meck:unload().
read_only_no_retry(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> 0 end),
meck:expect(erlfdb, get, fun(_, _) -> foo end),
meck:expect(erlfdb, is_read_only, fun(_) -> true end),
meck:expect(fabric2_txids, remove, fun(undefined) -> ok end),
Result = fabric2_fdb:transactional(fun(Tx) ->
?assertEqual(foo, erlfdb:get(Tx, bar)),
did_run
end),
?assertEqual(did_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
read_only_commit_unknown_result(_) ->
% Not 100% certain that this would ever actually
% happen in the wild but might as well test that
% we don't blow up if it does.
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> 1021 end),
meck:expect(erlfdb, get, fun(_, _) -> foo end),
meck:expect(erlfdb, is_read_only, fun(_) -> true end),
meck:expect(fabric2_txids, remove, fun(undefined) -> ok end),
Result = fabric2_fdb:transactional(fun(Tx) ->
?assertEqual(foo, erlfdb:get(Tx, bar)),
did_run
end),
?assertEqual(did_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
run_on_first_try(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> undefined end),
meck:expect(erlfdb, clear, fun(_, _) -> ok end),
meck:expect(erlfdb, is_read_only, fun(_) -> false end),
meck:expect(fabric2_txids, create, fun(_, _) -> <<"a txid">> end),
meck:expect(erlfdb, set, fun(_, <<"a txid">>, <<>>) -> ok end),
meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
Result = fabric2_fdb:transactional(fun(Tx) ->
?assertEqual(ok, erlfdb:clear(Tx, bang)),
did_run
end),
?assertEqual(did_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
retry_when_commit_conflict(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> 1020 end),
meck:expect(erlfdb, clear, fun(_, _) -> ok end),
meck:expect(erlfdb, is_read_only, fun(_) -> false end),
meck:expect(fabric2_txids, create, fun(_, _) -> <<"a txid">> end),
meck:expect(erlfdb, set, fun(_, <<"a txid">>, <<>>) -> ok end),
meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
Result = fabric2_fdb:transactional(fun(Tx) ->
?assertEqual(ok, erlfdb:clear(Tx, <<"foo">>)),
did_run
end),
?assertEqual(did_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
retry_when_txid_not_found(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> 1021 end),
meck:expect(erlfdb, get, fun(_, <<"a txid">>) -> future end),
meck:expect(erlfdb, wait, fun(future) -> not_found end),
meck:expect(erlfdb, clear, fun(_, _) -> ok end),
meck:expect(erlfdb, is_read_only, fun(_) -> false end),
meck:expect(erlfdb, set, fun(_, <<"a txid">>, <<>>) -> ok end),
meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
put('$fabric_tx_id', <<"a txid">>),
put('$fabric_tx_result', not_the_correct_result),
Result = fabric2_fdb:transactional(fun(Tx) ->
?assertEqual(ok, erlfdb:clear(Tx, <<"foo">>)),
yay_not_skipped
end),
?assertEqual(yay_not_skipped, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
no_retry_when_txid_found(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
meck:expect(erlfdb, get_last_error, fun() -> 1021 end),
meck:expect(erlfdb, get, fun(_, <<"a txid">>) -> future end),
meck:expect(erlfdb, wait, fun(future) -> <<>> end),
meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
put('$fabric_tx_id', <<"a txid">>),
put('$fabric_tx_result', did_not_run),
Result = fabric2_fdb:transactional(fun(_Tx) ->
?assert(false),
did_run
end),
?assertEqual(did_not_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).
use_last_unknown_result(_) ->
meck:expect(erlfdb, transactional, fun(_Db, UserFun) ->
UserFun(not_a_real_transaction)
end),
% Set another retryable error as last error
meck:expect(erlfdb, get_last_error, fun() -> 1009 end),
meck:expect(erlfdb, get, fun(_, <<"a txid">>) -> future end),
meck:expect(erlfdb, wait, fun(future) -> <<>> end),
meck:expect(fabric2_txids, remove, fun(<<"a txid">>) -> ok end),
put('$fabric_tx_id', <<"a txid">>),
put('$fabric_tx_result', did_not_run),
put('$fabric_tx_res_was_unknown', true),
Result = fabric2_fdb:transactional(fun(_Tx) ->
?assert(false),
did_run
end),
?assertEqual(did_not_run, Result),
?assert(meck:validate([erlfdb, fabric2_txids])).