From b9fe803d169b958bdcef2e11e30d86cc3a1d4023 Mon Sep 17 00:00:00 2001 From: Nick Vatamaniuc Date: Tue, 2 Mar 2021 17:53:07 -0500 Subject: Allow applying per-transaction options with fabric2_fdb:transactional/3 1) First, as a cleanup, remove DB `Options` from the `init_db/3 call. We always follow `init_db/3` (sometimes called through the `fabric2_fdb:transactional(DbName, ...)` with a `create(TxDb, Options)` or `open(TxDb, Options)` call, where we overrode `Options` anyway. The only time we didn't follow it up with a `create/2` or `open/2` is when dbs are deleted where `Options` wouldn't matter. 2) Add a new `fabric2_fdb:transactional(DbName|Db, TxOptions, Fun)` call which allows specifying per-transaction TX options in the `TxOptions` arg. The format of `TxOptions` is `#{option_name_as_atom => integer | binary}` --- src/fabric/src/fabric2_db.erl | 8 ++++---- src/fabric/src/fabric2_fdb.erl | 44 ++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl index a310470ea..6d8bb72e9 100644 --- a/src/fabric/src/fabric2_db.erl +++ b/src/fabric/src/fabric2_db.erl @@ -173,7 +173,7 @@ create(DbName, Options) -> case validate_dbname(DbName) of ok -> - Result = fabric2_fdb:transactional(DbName, Options, fun(TxDb) -> + Result = fabric2_fdb:transactional(DbName, fun(TxDb) -> case fabric2_fdb:exists(TxDb) of true -> {error, file_exists}; @@ -205,7 +205,7 @@ open(DbName, Options) -> Db2 = maybe_set_interactive(Db1, Options), {ok, require_member_check(Db2)}; undefined -> - Result = fabric2_fdb:transactional(DbName, Options, fun(TxDb) -> + Result = fabric2_fdb:transactional(DbName, fun(TxDb) -> fabric2_fdb:open(TxDb, Options) end), % Cache outside the transaction retry loop @@ -227,7 +227,7 @@ delete(DbName, Options) -> Options1 = lists:keystore(user_ctx, 1, Options, ?ADMIN_CTX), case lists:keyfind(deleted_at, 1, Options1) of {deleted_at, TimeStamp} -> - fabric2_fdb:transactional(DbName, Options1, fun(TxDb) -> + fabric2_fdb:transactional(DbName, fun(TxDb) -> fabric2_fdb:remove_deleted_db(TxDb, TimeStamp) end); false -> @@ -245,7 +245,7 @@ delete(DbName, Options) -> undelete(DbName, TgtDbName, TimeStamp, Options) -> case validate_dbname(TgtDbName) of ok -> - Resp = fabric2_fdb:transactional(DbName, Options, + Resp = fabric2_fdb:transactional(DbName, fun(TxDb) -> fabric2_fdb:undelete(TxDb, TgtDbName, TimeStamp) end diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl index 1690f2f2c..df3107152 100644 --- a/src/fabric/src/fabric2_fdb.erl +++ b/src/fabric/src/fabric2_fdb.erl @@ -118,18 +118,25 @@ transactional(Fun) -> - do_transaction(Fun, undefined). + do_transaction(Fun, undefined, #{}). -transactional(DbName, Options, Fun) when is_binary(DbName) -> - with_span(Fun, #{'db.name' => DbName}, fun() -> - transactional(fun(Tx) -> - Fun(init_db(Tx, DbName, Options)) - end) - end). +transactional(DbName, Fun) when is_binary(DbName), is_function(Fun) -> + transactional(DbName, #{}, Fun); + +transactional(#{} = Db, Fun) when is_function(Fun) -> + transactional(Db, #{}, Fun). + -transactional(#{tx := undefined} = Db, Fun) -> +transactional(DbName, #{} = TxOptions, Fun) when is_binary(DbName) -> + with_span(Fun, #{'db.name' => DbName}, fun() -> + do_transaction(fun(Tx) -> + Fun(init_db(Tx, DbName)) + end, undefined, TxOptions) + end); + +transactional(#{tx := undefined} = Db, #{} = TxOptions, Fun) -> DbName = maps:get(name, Db, undefined), try Db1 = refresh(Db), @@ -145,30 +152,31 @@ transactional(#{tx := undefined} = Db, Fun) -> true -> Fun(reopen(Db2#{tx => Tx})); false -> Fun(Db2#{tx => Tx}) end - end, LayerPrefix) + end, LayerPrefix, TxOptions) end) catch throw:{?MODULE, reopen} -> with_span('db.reopen', #{'db.name' => DbName}, fun() -> transactional(Db#{reopen => true}, Fun) end) end; -transactional(#{tx := {erlfdb_snapshot, _}} = Db, Fun) -> +transactional(#{tx := {erlfdb_snapshot, _}} = Db, #{} = _TxOptions, Fun) -> DbName = maps:get(name, Db, undefined), with_span(Fun, #{'db.name' => DbName}, fun() -> Fun(Db) end); -transactional(#{tx := {erlfdb_transaction, _}} = Db, Fun) -> +transactional(#{tx := {erlfdb_transaction, _}} = Db, #{} = _TxOptions, Fun) -> DbName = maps:get(name, Db, undefined), with_span(Fun, #{'db.name' => DbName}, fun() -> Fun(Db) end). -do_transaction(Fun, LayerPrefix) when is_function(Fun, 1) -> +do_transaction(Fun, LayerPrefix, #{} = TxOptions) when is_function(Fun, 1) -> Db = get_db_handle(), try erlfdb:transactional(Db, fun(Tx) -> + apply_tx_options(Tx, TxOptions), case get(erlfdb_trace) of Name when is_binary(Name) -> UId = erlang:unique_integer([positive]), @@ -190,6 +198,12 @@ do_transaction(Fun, LayerPrefix) when is_function(Fun, 1) -> end. +apply_tx_options(Tx, #{} = TxOptions) -> + maps:map(fun(K, V) -> + erlfdb:set_option(Tx, K, V) + end, TxOptions). + + with_snapshot(#{tx := {erlfdb_transaction, _} = Tx} = TxDb, Fun) -> SSDb = TxDb#{tx := erlfdb:snapshot(Tx)}, Fun(SSDb); @@ -365,7 +379,7 @@ reopen(#{} = OldDb) -> interactive := Interactive } = OldDb, Options1 = lists:keystore(user_ctx, 1, Options, {user_ctx, UserCtx}), - NewDb = open(init_db(Tx, DbName, Options1), Options1), + NewDb = open(init_db(Tx, DbName), Options1), % Check if database was re-created case {Interactive, maps:get(uuid, NewDb)} of @@ -1226,7 +1240,7 @@ debug_cluster(Start, End) -> end). -init_db(Tx, DbName, Options) -> +init_db(Tx, DbName) -> Prefix = get_dir(Tx), Version = erlfdb:wait(erlfdb:get(Tx, ?METADATA_VERSION_KEY)), #{ @@ -1236,7 +1250,7 @@ init_db(Tx, DbName, Options) -> md_version => Version, security_fun => undefined, - db_options => Options + db_options => [] }. -- cgit v1.2.1