diff --git a/README.md b/README.md index 3cfaee3..1ee59b8 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,18 @@ depend on having an up to date size count at all times, you need to maintain it yourself. If you only need the size occasionally, you may traverse the table to count the elements. +When `mrdb` transactions abort, they will return a stacktrace caught +from within the transaction fun, giving much better debugging info. +This is different from how mnesia does it. + +If behavior closer to mnesia's abort returns are needed, say, for backwards +compatibility, this can be controlled by setting the environment variable +`-mnesia_rocksdb mnesia_compatible_aborts true`, or by adding a transaction +option, e.g. `mrdb:activity({tx, #{mnesia_compatible => true}}, fun() ... end)`. +For really performance-critical transactions which may abort often, it might +make a difference to set this option to `true`, since there is a cost involved +in producing stacktraces. + ### Mnesia backend plugins ### diff --git a/src/mrdb.erl b/src/mrdb.erl index fb9d431..9be0a4a 100644 --- a/src/mrdb.erl +++ b/src/mrdb.erl @@ -297,14 +297,34 @@ do_activity(F, Alias, Ctxt) -> end. try_f(F, Ctxt) -> + try_f(mnesia_compatible_aborts(Ctxt), F, Ctxt). + +try_f(false, F, Ctxt) -> try run_f(F, Ctxt) of Res -> commit_and_pop(Res) catch + throw:Something -> + abort_and_pop(throw, Something); + Cat:Err:T -> + %% Without capturing the stacktract here, + %% debugging gets pretty difficult. Incompatible with mnesia, though. + abort_and_pop(Cat, {Err, T}) + end; +try_f(true, F, Ctxt) -> + try run_f(F, Ctxt) of + Res -> + commit_and_pop(Res) + catch + throw:Something -> + abort_and_pop(throw, Something); Cat:Err -> + %% Without capturing the stacktract here, + %% debugging gets pretty difficult abort_and_pop(Cat, Err) end. + run_f(F, Ctxt) -> push_ctxt(Ctxt), F(). @@ -411,7 +431,7 @@ apply_tx_opts(Opts0) when is_map(Opts0) -> check_tx_opts(maps:merge(default_tx_opts(), Opts0)). check_tx_opts(Opts) -> - case maps:without([no_snapshot, retries], Opts) of + case maps:without([no_snapshot, retries, mnesia_compatible], Opts) of Other when map_size(Other) > 0 -> abort({invalid_tx_opts, maps:keys(Other)}); _ -> @@ -523,6 +543,11 @@ re_throw(Cat, Err) -> mnesia_compatible_aborts() -> mnesia_rocksdb_admin:get_cached_env(mnesia_compatible_aborts, false). +mnesia_compatible_aborts(#{activity := #{mnesia_compatible := Bool}}) -> + Bool; +mnesia_compatible_aborts(_) -> + mnesia_compatible_aborts(). + fix_error({aborted, Err}) -> Err; fix_error(Err) -> diff --git a/test/mnesia_rocksdb_SUITE.erl b/test/mnesia_rocksdb_SUITE.erl index d0fa050..7bba6ab 100644 --- a/test/mnesia_rocksdb_SUITE.erl +++ b/test/mnesia_rocksdb_SUITE.erl @@ -269,7 +269,7 @@ mrdb_abort(Config) -> Pre = mrdb:read(tx_abort, a), D0 = get_dict(), TRes = try mrdb:activity( - tx, rdb, + {tx, #{mnesia_compatible => true}}, rdb, fun() -> [{tx_abort, a, N}] = mrdb:read(tx_abort, a), error(abort_here),