Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce0c09aec6 | |||
| 6c1a343c82 | |||
| 6246f3f2e1 | |||
| c6969238c7 | |||
| d6a4dca5f6 | |||
| b97b727605 |
+5
-2
@@ -4,7 +4,7 @@
|
|||||||
{deps,
|
{deps,
|
||||||
[
|
[
|
||||||
{sext, "1.8.0"},
|
{sext, "1.8.0"},
|
||||||
{rocksdb, {git, "https://github.com/emqx/erlang-rocksdb.git", {ref,"d695c6e"}}},
|
{rocksdb, {git, "https://github.com/emqx/erlang-rocksdb.git", {ref, "09df04e"}}},
|
||||||
{hut, "1.4.0"}
|
{hut, "1.4.0"}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
@@ -26,7 +26,10 @@
|
|||||||
, {meck, "0.9.2"}
|
, {meck, "0.9.2"}
|
||||||
, {trace_runner, {git, "https://github.com/uwiger/trace_runner.git",
|
, {trace_runner, {git, "https://github.com/uwiger/trace_runner.git",
|
||||||
{ref, "2e56677"}}}
|
{ref, "2e56677"}}}
|
||||||
]}
|
]},
|
||||||
|
{dialyzer, [{plt_apps, all_deps},
|
||||||
|
{base_plt_apps, [erts, kernel, stdlib, mnesia, runtime_tools, eunit,
|
||||||
|
proper, trace_runner, common_test]}]}
|
||||||
]},
|
]},
|
||||||
{edown,
|
{edown,
|
||||||
%% Use as `rebar3 as edown do edoc`
|
%% Use as `rebar3 as edown do edoc`
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
[{<<"hut">>,{pkg,<<"hut">>,<<"1.4.0">>},0},
|
[{<<"hut">>,{pkg,<<"hut">>,<<"1.4.0">>},0},
|
||||||
{<<"rocksdb">>,
|
{<<"rocksdb">>,
|
||||||
{git,"https://github.com/emqx/erlang-rocksdb.git",
|
{git,"https://github.com/emqx/erlang-rocksdb.git",
|
||||||
{ref,"d695c6ee9dd27bfe492ed4e24c72ad20ab0d770b"}},
|
{ref,"09df04e02f250a5075bb93cafdd3b637cab81d96"}},
|
||||||
0},
|
0},
|
||||||
{<<"sext">>,{pkg,<<"sext">>,<<"1.8.0">>},0}]}.
|
{<<"sext">>,{pkg,<<"sext">>,<<"1.8.0">>},0}]}.
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
| {add_aliases, [alias()]}
|
| {add_aliases, [alias()]}
|
||||||
| {write_table_property, tabname(), tuple()}
|
| {write_table_property, tabname(), tuple()}
|
||||||
| {remove_aliases, [alias()]}
|
| {remove_aliases, [alias()]}
|
||||||
| {migrate, [{tabname(), map()}], rpt()}
|
| {migrate, [tabname() | {tabname(), map()}], rpt()}
|
||||||
| {prep_close, table()}
|
| {prep_close, table()}
|
||||||
| {close_table, table()}
|
| {close_table, table()}
|
||||||
| {clear_table, table() | cf() }.
|
| {clear_table, table() | cf() }.
|
||||||
@@ -272,9 +272,16 @@ delete_info_(Ref, Tab, K) ->
|
|||||||
write_table_property(Alias, Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 ->
|
write_table_property(Alias, Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 ->
|
||||||
call(Alias, {write_table_property, Tab, Prop}).
|
call(Alias, {write_table_property, Tab, Prop}).
|
||||||
|
|
||||||
|
-spec migrate_standalone(alias(), Tabs) -> Res when
|
||||||
|
Tabs :: [tabname() | {tabname(),map()}],
|
||||||
|
Res :: [{tabname(), {ok,any()} | {error, any()}}].
|
||||||
migrate_standalone(Alias, Tabs) ->
|
migrate_standalone(Alias, Tabs) ->
|
||||||
migrate_standalone(Alias, Tabs, undefined).
|
migrate_standalone(Alias, Tabs, undefined).
|
||||||
|
|
||||||
|
-spec migrate_standalone(alias(), Tabs, Rpt) -> Res when
|
||||||
|
Tabs :: [tabname() | {tabname(),map()}],
|
||||||
|
Rpt :: 'undefined' | pid() | atom(),
|
||||||
|
Res :: [{tabname(), {ok,any()} | {error, any()}}].
|
||||||
migrate_standalone(Alias, Tabs, Rpt0) ->
|
migrate_standalone(Alias, Tabs, Rpt0) ->
|
||||||
Rpt = case Rpt0 of
|
Rpt = case Rpt0 of
|
||||||
undefined -> undefined;
|
undefined -> undefined;
|
||||||
|
|||||||
+100
-18
@@ -68,6 +68,7 @@
|
|||||||
, select/2 , select/3
|
, select/2 , select/3
|
||||||
, select_reverse/2, select_reverse/3
|
, select_reverse/2, select_reverse/3
|
||||||
, select/1
|
, select/1
|
||||||
|
, ms/2
|
||||||
, fold/3 , fold/4 , fold/5
|
, fold/3 , fold/4 , fold/5
|
||||||
, fold_reverse/3 , fold_reverse/4, fold_reverse/5
|
, fold_reverse/3 , fold_reverse/4, fold_reverse/5
|
||||||
, rdb_fold/4 , rdb_fold/5
|
, rdb_fold/4 , rdb_fold/5
|
||||||
@@ -98,12 +99,18 @@
|
|||||||
, encode_val/2
|
, encode_val/2
|
||||||
, decode_val/3 ]).
|
, decode_val/3 ]).
|
||||||
|
|
||||||
|
%% TODO: OTP 28.3 dialyzer is quite pesky about opaque types. Find a way
|
||||||
|
%% to structure this, and firm up the type specs.
|
||||||
|
-dialyzer([no_opaque, no_return]).
|
||||||
|
-dialyzer([{nowarn_function, [activity/3]}]).
|
||||||
|
|
||||||
-export_type( [ mrdb_iterator/0
|
-export_type( [ mrdb_iterator/0
|
||||||
, itr_handle/0
|
, itr_handle/0
|
||||||
, iterator_action/0
|
, iterator_action/0
|
||||||
, db_ref/0
|
, db_ref/0
|
||||||
, ref_or_tab/0
|
, ref_or_tab/0
|
||||||
, index_position/0
|
, index_position/0
|
||||||
|
, match_pattern/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include("mnesia_rocksdb.hrl").
|
-include("mnesia_rocksdb.hrl").
|
||||||
@@ -125,6 +132,9 @@
|
|||||||
-type outer() :: non_neg_integer().
|
-type outer() :: non_neg_integer().
|
||||||
-type retries() :: outer() | {inner(), outer()}.
|
-type retries() :: outer() | {inner(), outer()}.
|
||||||
|
|
||||||
|
-type matchpat_map() :: #{atom() => _}.
|
||||||
|
-type match_pattern() :: matchpat_map() | ets:match_pattern().
|
||||||
|
|
||||||
%% activity type 'ets' makes no sense in this context
|
%% activity type 'ets' makes no sense in this context
|
||||||
-type mnesia_activity_type() :: transaction
|
-type mnesia_activity_type() :: transaction
|
||||||
| sync_transaction
|
| sync_transaction
|
||||||
@@ -132,14 +142,15 @@
|
|||||||
| sync_dirty.
|
| sync_dirty.
|
||||||
|
|
||||||
-type tx_options() :: #{ retries => retries()
|
-type tx_options() :: #{ retries => retries()
|
||||||
, no_snapshot => boolean() }.
|
, no_snapshot => boolean()
|
||||||
|
, mnesia_compatible => boolean() }.
|
||||||
-type mrdb_activity_type() :: tx | {tx, tx_options()} | batch.
|
-type mrdb_activity_type() :: tx | {tx, tx_options()} | batch.
|
||||||
|
|
||||||
-type activity_type() :: mrdb_activity_type() | mnesia_activity_type().
|
-type activity_type() :: mrdb_activity_type() | mnesia_activity_type().
|
||||||
|
|
||||||
-type key() :: any().
|
-type key() :: any().
|
||||||
-type obj() :: tuple().
|
-type obj() :: tuple().
|
||||||
-type index_position() :: atom() | pos().
|
-type index_position() :: atom() | pos() | plugin_ix_pos().
|
||||||
|
|
||||||
-type db_handle() :: rocksdb:db_handle().
|
-type db_handle() :: rocksdb:db_handle().
|
||||||
-type cf_handle() :: rocksdb:cf_handle().
|
-type cf_handle() :: rocksdb:cf_handle().
|
||||||
@@ -155,10 +166,14 @@
|
|||||||
-type activity() :: tx_activity() | batch_activity().
|
-type activity() :: tx_activity() | batch_activity().
|
||||||
|
|
||||||
-type pos() :: non_neg_integer().
|
-type pos() :: non_neg_integer().
|
||||||
|
-type plugin_ix_pos() :: {atom()}.
|
||||||
|
|
||||||
|
-type propkey() :: any().
|
||||||
|
-type propvalue() :: any().
|
||||||
-type properties() :: #{ record_name := atom()
|
-type properties() :: #{ record_name := atom()
|
||||||
, attributes := [atom()]
|
, attributes := [atom()]
|
||||||
, index := [{pos(), bag | ordered}]
|
, index := [{pos(), bag | ordered}]
|
||||||
|
, user_properties => #{propkey() => propvalue()}
|
||||||
}.
|
}.
|
||||||
-type semantics() :: bag | set.
|
-type semantics() :: bag | set.
|
||||||
-type key_encoding() :: 'raw' | 'sext' | 'term'.
|
-type key_encoding() :: 'raw' | 'sext' | 'term'.
|
||||||
@@ -169,7 +184,7 @@
|
|||||||
-type attr_pos() :: #{atom() := pos()}.
|
-type attr_pos() :: #{atom() := pos()}.
|
||||||
|
|
||||||
-type db_ref() :: #{ name => table()
|
-type db_ref() :: #{ name => table()
|
||||||
, alias => atom()
|
, alias => alias()
|
||||||
, vsn => non_neg_integer()
|
, vsn => non_neg_integer()
|
||||||
, db_ref := db_handle()
|
, db_ref := db_handle()
|
||||||
, cf_handle := cf_handle()
|
, cf_handle := cf_handle()
|
||||||
@@ -184,6 +199,11 @@
|
|||||||
, activity => activity()
|
, activity => activity()
|
||||||
, _ => _}.
|
, _ => _}.
|
||||||
|
|
||||||
|
-type context() :: #{ 'activity' => activity()
|
||||||
|
, 'alias' => alias()
|
||||||
|
, 'retries' => retries()
|
||||||
|
, 'db_ref' => db_ref() }.
|
||||||
|
|
||||||
-type error() :: {error, any()}.
|
-type error() :: {error, any()}.
|
||||||
|
|
||||||
-type ref_or_tab() :: table() | db_ref().
|
-type ref_or_tab() :: table() | db_ref().
|
||||||
@@ -275,7 +295,7 @@ release_snapshot(SHandle) ->
|
|||||||
%% To simplify code adaptation, `tx | transaction | sync_transaction' are synonyms, and
|
%% To simplify code adaptation, `tx | transaction | sync_transaction' are synonyms, and
|
||||||
%% `batch | async_dirty | sync_dirty' are synonyms.
|
%% `batch | async_dirty | sync_dirty' are synonyms.
|
||||||
%% @end
|
%% @end
|
||||||
-spec activity(activity_type(), alias(), fun( () -> Res )) -> Res.
|
-spec activity(activity_type(), alias(), fun( () -> any() )) -> any().
|
||||||
activity(Type, Alias, F) ->
|
activity(Type, Alias, F) ->
|
||||||
#{db_ref := DbRef} = ensure_ref({admin, Alias}),
|
#{db_ref := DbRef} = ensure_ref({admin, Alias}),
|
||||||
Ctxt = case tx_type(Type) of
|
Ctxt = case tx_type(Type) of
|
||||||
@@ -293,6 +313,7 @@ activity(Type, Alias, F) ->
|
|||||||
end,
|
end,
|
||||||
do_activity(F, Alias, Ctxt).
|
do_activity(F, Alias, Ctxt).
|
||||||
|
|
||||||
|
-spec do_activity(fun(() -> any()), alias(), context()) -> any().
|
||||||
do_activity(F, Alias, Ctxt) ->
|
do_activity(F, Alias, Ctxt) ->
|
||||||
try try_f(F, Ctxt)
|
try try_f(F, Ctxt)
|
||||||
catch
|
catch
|
||||||
@@ -309,11 +330,11 @@ try_f(false, F, Ctxt) ->
|
|||||||
commit_and_pop(Res)
|
commit_and_pop(Res)
|
||||||
catch
|
catch
|
||||||
throw:Something ->
|
throw:Something ->
|
||||||
abort_and_pop(throw, Something);
|
abort_and_pop(false, throw, Something);
|
||||||
Cat:Err:T ->
|
Cat:Err:T ->
|
||||||
%% Without capturing the stacktract here,
|
%% Without capturing the stacktract here,
|
||||||
%% debugging gets pretty difficult. Incompatible with mnesia, though.
|
%% debugging gets pretty difficult. Incompatible with mnesia, though.
|
||||||
abort_and_pop(Cat, {Err, T})
|
abort_and_pop(false, Cat, {Err, T})
|
||||||
end;
|
end;
|
||||||
try_f(true, F, Ctxt) ->
|
try_f(true, F, Ctxt) ->
|
||||||
try run_f(F, Ctxt) of
|
try run_f(F, Ctxt) of
|
||||||
@@ -321,11 +342,11 @@ try_f(true, F, Ctxt) ->
|
|||||||
commit_and_pop(Res)
|
commit_and_pop(Res)
|
||||||
catch
|
catch
|
||||||
throw:Something ->
|
throw:Something ->
|
||||||
abort_and_pop(throw, Something);
|
abort_and_pop(true, throw, Something);
|
||||||
Cat:Err ->
|
Cat:Err ->
|
||||||
%% Without capturing the stacktract here,
|
%% Without capturing the stacktract here,
|
||||||
%% debugging gets pretty difficult
|
%% debugging gets pretty difficult
|
||||||
abort_and_pop(Cat, Err)
|
abort_and_pop(true, Cat, Err)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@@ -358,7 +379,7 @@ retry_activity(F, Alias, #{activity := #{ type := Type
|
|||||||
retry_activity(F, Alias, Ctxt1)
|
retry_activity(F, Alias, Ctxt1)
|
||||||
end;
|
end;
|
||||||
error ->
|
error ->
|
||||||
return_abort(Type, error, retry_limit)
|
return_abort(mnesia_compatible_aborts(Ctxt), Type, error, retry_limit)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
retry_activity_(inner, F, Alias, Ctxt) ->
|
retry_activity_(inner, F, Alias, Ctxt) ->
|
||||||
@@ -415,6 +436,7 @@ current_context() ->
|
|||||||
undefined
|
undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec tx_type(activity_type()) -> {'tx', map()} | 'batch'.
|
||||||
tx_type(T) ->
|
tx_type(T) ->
|
||||||
case T of
|
case T of
|
||||||
_ when T==batch;
|
_ when T==batch;
|
||||||
@@ -501,8 +523,8 @@ commit_and_pop(Res) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec abort_and_pop(atom(), any()) -> no_return().
|
-spec abort_and_pop(boolean(), atom(), any()) -> no_return().
|
||||||
abort_and_pop(Cat, Err) ->
|
abort_and_pop(Compat, Cat, Err) ->
|
||||||
%% We can pop the context right away, since there is no
|
%% We can pop the context right away, since there is no
|
||||||
%% complex failure handling (like retry-on-busy) for rollback.
|
%% complex failure handling (like retry-on-busy) for rollback.
|
||||||
#{activity := #{type := Type, handle := H}} = pop_ctxt(),
|
#{activity := #{type := Type, handle := H}} = pop_ctxt(),
|
||||||
@@ -510,23 +532,29 @@ abort_and_pop(Cat, Err) ->
|
|||||||
tx -> ok = rdb_transaction_rollback(H);
|
tx -> ok = rdb_transaction_rollback(H);
|
||||||
batch -> ok = release_batches(H)
|
batch -> ok = release_batches(H)
|
||||||
end,
|
end,
|
||||||
return_abort(Type, Cat, Err).
|
return_abort(Compat, Type, Cat, Err).
|
||||||
|
|
||||||
-spec return_abort(batch | tx, atom(), any()) -> no_return().
|
-spec return_abort(boolean(), batch | tx, atom(), any()) -> no_return().
|
||||||
return_abort(batch, Cat, Err) ->
|
return_abort(_, batch, Cat, Err) ->
|
||||||
re_throw(Cat, Err);
|
re_throw(Cat, Err);
|
||||||
return_abort(tx, Cat, Err) ->
|
return_abort(Compat, tx, Cat, Err) ->
|
||||||
case mnesia_compatible_aborts() of
|
case Compat of
|
||||||
true ->
|
true ->
|
||||||
%% Mnesia always captures stack traces, but this could actually become a
|
%% Mnesia always captures stack traces, but this could actually become a
|
||||||
%% performance issue in some cases (generally, it's better not to lazily
|
%% performance issue in some cases (generally, it's better not to lazily
|
||||||
%% produce stack traces.) Since we want to pushe the option checking for
|
%% produce stack traces.) Since we want to push the option checking for
|
||||||
%% mnesia-abort-style compatibility to AFTER detecting an abort, we don't
|
%% mnesia-abort-style compatibility to AFTER detecting an abort, we don't
|
||||||
%% order a stack trace initially, and instead insert an empty list.
|
%% order a stack trace initially, and instead insert an empty list.
|
||||||
%% (The exact stack trace wouldn't be the same anyway.)
|
%% (The exact stack trace wouldn't be the same anyway.)
|
||||||
|
%% NOTE: This behavior changed in mnesia-4.23.5.1, so if we really want
|
||||||
|
%% to stay compatible, we have to special-case things.
|
||||||
Err1 =
|
Err1 =
|
||||||
case Cat of
|
case Cat of
|
||||||
error -> {fix_error(Err), []};
|
error ->
|
||||||
|
case newer_mnesia() of
|
||||||
|
true -> fix_error(Err);
|
||||||
|
false -> {fix_error(Err), []}
|
||||||
|
end;
|
||||||
exit -> fix_error(Err);
|
exit -> fix_error(Err);
|
||||||
throw -> {throw, Err}
|
throw -> {throw, Err}
|
||||||
end,
|
end,
|
||||||
@@ -535,6 +563,11 @@ return_abort(tx, Cat, Err) ->
|
|||||||
re_throw(Cat, Err)
|
re_throw(Cat, Err)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% Some rollback return values changed in mnesia in vsn 4.23.5.1
|
||||||
|
newer_mnesia() ->
|
||||||
|
{ok, Vsn} = application:get_key(mnesia, vsn),
|
||||||
|
("4.23.5" < Vsn).
|
||||||
|
|
||||||
-spec re_throw(atom(), any()) -> no_return().
|
-spec re_throw(atom(), any()) -> no_return().
|
||||||
re_throw(Cat, Err) ->
|
re_throw(Cat, Err) ->
|
||||||
case Cat of
|
case Cat of
|
||||||
@@ -1327,6 +1360,55 @@ select_reverse(Tab, Pat, Limit) when Limit == infinity; is_integer(Limit), Limit
|
|||||||
select(Cont) ->
|
select(Cont) ->
|
||||||
mrdb_select:select(Cont).
|
mrdb_select:select(Cont).
|
||||||
|
|
||||||
|
%% @doc Produce a match specification for select(), supporting map-based match patterns
|
||||||
|
%%
|
||||||
|
%% Using record syntax in match patterns tends to conflict with type checking. This
|
||||||
|
%% function offers an alternative approach, drawing on the fact that mnesia_rocksdb
|
||||||
|
%% keeps the record name and attribute names readily available as persistent terms.
|
||||||
|
%%
|
||||||
|
%% When using the map-based representation, the match pattern is built by matching
|
||||||
|
%% attribute names to map elements; any attribute not found in the map gets set to '_'.
|
||||||
|
%% Thus, ```[{#balance{key = {Acct,'$1'},_='_'},[{'>=','$1',Height}],['$_']}]''' can be
|
||||||
|
%% created as ```ms(balance,[{#{key => {Acct,'$1'}},[{'>=','$1',Height}],['$_']}])'''.
|
||||||
|
%%
|
||||||
|
%% This has the advantage over `ms_transform' that it can handle bound variables
|
||||||
|
%% in the match pattern. The function works on all mnesia table types.
|
||||||
|
%% @end
|
||||||
|
-spec ms(ref_or_tab(), [{match_pattern(), [_], [_]}]) -> ets:match_spec().
|
||||||
|
ms(Tab, Pat) ->
|
||||||
|
#{ attributes := Attrs
|
||||||
|
, record_name := RecName } = any_tab_props(Tab),
|
||||||
|
[{headpat(RecName, Attrs, Hd), Gs, Body}
|
||||||
|
|| {Hd, Gs, Body} <- Pat].
|
||||||
|
|
||||||
|
any_tab_props(Tab) ->
|
||||||
|
try mrdb_props(Tab)
|
||||||
|
catch
|
||||||
|
exit:{aborted,{bad_type,_}} ->
|
||||||
|
mnesia_props(Tab)
|
||||||
|
end.
|
||||||
|
|
||||||
|
mrdb_props(Tab) ->
|
||||||
|
#{properties := Props} = get_ref(Tab),
|
||||||
|
Props.
|
||||||
|
|
||||||
|
mnesia_props(Tab) ->
|
||||||
|
try mnesia_props_(Tab)
|
||||||
|
catch
|
||||||
|
exit:{aborted, _} ->
|
||||||
|
mnesia:abort({bad_type, Tab})
|
||||||
|
end.
|
||||||
|
|
||||||
|
mnesia_props_(Tab) ->
|
||||||
|
#{ record_name => mnesia:table_info(Tab, record_name)
|
||||||
|
, attributes => mnesia:table_info(Tab, attributes) }.
|
||||||
|
|
||||||
|
headpat(RecName, Attrs, Hd) when is_map(Hd) ->
|
||||||
|
list_to_tuple([RecName | [maps:get(A, Hd, '_')
|
||||||
|
|| A <- Attrs]]);
|
||||||
|
headpat(_, _, Hd) when is_tuple(Hd); is_atom(Hd) ->
|
||||||
|
Hd.
|
||||||
|
|
||||||
clear_table(Tab) ->
|
clear_table(Tab) ->
|
||||||
match_delete(Tab, '_').
|
match_delete(Tab, '_').
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
%% -------------------------------------------------------------------
|
|
||||||
%%
|
|
||||||
%% basho_bench: Benchmarking Suite
|
|
||||||
%%
|
|
||||||
%% Copyright (c) 2009-2010 Basho Techonologies
|
|
||||||
%%
|
|
||||||
%% This file is provided to you 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(basho_bench_driver_mnesia_rocksdb).
|
|
||||||
|
|
||||||
-export([new/1,
|
|
||||||
run/4]).
|
|
||||||
|
|
||||||
-include("mnesia_rocksdb_basho_bench.hrl").
|
|
||||||
|
|
||||||
%% ====================================================================
|
|
||||||
%% API
|
|
||||||
%% ====================================================================
|
|
||||||
|
|
||||||
new(_Id) ->
|
|
||||||
Type = basho_bench_config:get(backend, ram_copies),
|
|
||||||
Tab = basho_bench_config:get(mnesia_table, t),
|
|
||||||
ok = bootstrap_mnesia(Tab, Type),
|
|
||||||
{ok, Tab}.
|
|
||||||
|
|
||||||
bootstrap_mnesia(Tab, Type) ->
|
|
||||||
ok = mnesia:create_schema([node()],
|
|
||||||
[{backend_types,
|
|
||||||
[{rocksdb_copies, mnesia_rocksdb}]}]),
|
|
||||||
ok = mnesia:start(),
|
|
||||||
{atomic,ok} = mnesia:create_table(Tab, [{Type, [node()]}]),
|
|
||||||
mnesia:wait_for_tables([Tab], 10000).
|
|
||||||
|
|
||||||
run(get, KeyGen, _ValueGen, State) ->
|
|
||||||
Tab = State,
|
|
||||||
Key = KeyGen(),
|
|
||||||
case mnesia:dirty_read({Tab, Key}) of
|
|
||||||
[] ->
|
|
||||||
{ok, State};
|
|
||||||
[{_, Key, _}] ->
|
|
||||||
{ok, State}
|
|
||||||
end;
|
|
||||||
run(put, KeyGen, ValueGen, State) ->
|
|
||||||
Tab = State,
|
|
||||||
ok = mnesia:dirty_write({Tab, KeyGen(), ValueGen()}),
|
|
||||||
{ok, State};
|
|
||||||
run(delete, KeyGen, _ValueGen, State) ->
|
|
||||||
Tab = State,
|
|
||||||
ok = mnesia:dirty_delete({Tab, KeyGen()}),
|
|
||||||
{ok, State}.
|
|
||||||
@@ -216,6 +216,7 @@ mrdb_transactions_(Config) ->
|
|||||||
delete_tabs(Created),
|
delete_tabs(Created),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
-dialyzer(no_return).
|
||||||
mrdb_abort_reasons(_Config) ->
|
mrdb_abort_reasons(_Config) ->
|
||||||
Prev = mnesia_rocksdb_admin:set_and_cache_env(mnesia_compatible_aborts, true),
|
Prev = mnesia_rocksdb_admin:set_and_cache_env(mnesia_compatible_aborts, true),
|
||||||
X = some_value,
|
X = some_value,
|
||||||
@@ -234,6 +235,7 @@ compare_txs(Type, F) ->
|
|||||||
ct:log("Mrdb = ~p/~p", [Type, EMr]),
|
ct:log("Mrdb = ~p/~p", [Type, EMr]),
|
||||||
case {Type, EMn, EMr} of
|
case {Type, EMn, EMr} of
|
||||||
{error, {some_value, [_|_]}, {some_value, []}} -> ok;
|
{error, {some_value, [_|_]}, {some_value, []}} -> ok;
|
||||||
|
{error, E, E} -> ok;
|
||||||
{throw, {throw, some_value}, {throw, some_value}} -> ok;
|
{throw, {throw, some_value}, {throw, some_value}} -> ok;
|
||||||
{exit, some_value, some_value} -> ok;
|
{exit, some_value, some_value} -> ok;
|
||||||
{abort, some_value, some_value} -> ok
|
{abort, some_value, some_value} -> ok
|
||||||
@@ -276,21 +278,30 @@ mrdb_abort(Config) ->
|
|||||||
mrdb:insert(tx_abort, {tx_abort, a, 1}),
|
mrdb:insert(tx_abort, {tx_abort, a, 1}),
|
||||||
Pre = mrdb:read(tx_abort, a),
|
Pre = mrdb:read(tx_abort, a),
|
||||||
D0 = get_dict(),
|
D0 = get_dict(),
|
||||||
|
ActivityF = fun() ->
|
||||||
|
[{tx_abort, a, N}] = mrdb:read(tx_abort, a),
|
||||||
|
error(abort_here),
|
||||||
|
ok = mrdb:insert(tx_abort, [{tx_abort, a, N+1}]),
|
||||||
|
noooo
|
||||||
|
end,
|
||||||
TRes = try mrdb:activity(
|
TRes = try mrdb:activity(
|
||||||
{tx, #{mnesia_compatible => true}}, rdb,
|
{tx, #{mnesia_compatible => true}}, rdb,
|
||||||
fun() ->
|
ActivityF)
|
||||||
[{tx_abort, a, N}] = mrdb:read(tx_abort, a),
|
|
||||||
error(abort_here),
|
|
||||||
ok = mrdb:insert(tx_abort, [{tx_abort, a, N+1}]),
|
|
||||||
noooo
|
|
||||||
end)
|
|
||||||
catch
|
catch
|
||||||
error:abort_here ->
|
exit:{aborted, {abort_here, []}} ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
dictionary_unchanged(D0),
|
dictionary_unchanged(D0),
|
||||||
ok = TRes,
|
ok = TRes,
|
||||||
Pre = mrdb:read(tx_abort, a),
|
Pre = mrdb:read(tx_abort, a),
|
||||||
|
TRes1 = try mrdb:activity(tx, rdb, ActivityF)
|
||||||
|
catch
|
||||||
|
error:{abort_here, [_|_]} ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
dictionary_unchanged(D0),
|
||||||
|
ok = TRes1,
|
||||||
|
Pre = mrdb:read(tx_abort, a),
|
||||||
delete_tabs(Created),
|
delete_tabs(Created),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
-define(FAIL_MSG(Str, Args), ?ERROR(Str, Args), basho_bench_app:halt_or_kill()).
|
|
||||||
-define(STD_ERR(Str, Args), io:format(standard_error, Str, Args)).
|
|
||||||
|
|
||||||
-define(CONSOLE(Str, Args), lager:info(Str, Args)).
|
|
||||||
|
|
||||||
-define(DEBUG(Str, Args), lager:debug(Str, Args)).
|
|
||||||
-define(INFO(Str, Args), lager:info(Str, Args)).
|
|
||||||
-define(WARN(Str, Args), lager:warning(Str, Args)).
|
|
||||||
-define(ERROR(Str, Args), lager:error(Str, Args)).
|
|
||||||
|
|
||||||
-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
|
|
||||||
|
|
||||||
-define(VAL_GEN_BLOB_CFG, value_generator_blob_file).
|
|
||||||
-define(VAL_GEN_SRC_SIZE, value_generator_source_size).
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{mode, max}.
|
|
||||||
|
|
||||||
{duration, 10}.
|
|
||||||
|
|
||||||
{concurrent, 1}.
|
|
||||||
|
|
||||||
{driver, basho_bench_driver_mnesia_rocksdb}.
|
|
||||||
|
|
||||||
{key_generator, {int_to_bin,{uniform_int, 5000000}}}.
|
|
||||||
|
|
||||||
{value_generator, {fixed_bin, 10000}}.
|
|
||||||
|
|
||||||
{operations, [{get, 2}, {put, 2}, {delete, 1}]}.
|
|
||||||
|
|
||||||
{code_paths, []}.
|
|
||||||
|
|
||||||
{mnesia_table, doc}.
|
|
||||||
{backend, disc_only_copies}.
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{mode, max}.
|
|
||||||
|
|
||||||
{duration, 10}.
|
|
||||||
|
|
||||||
{concurrent, 1}.
|
|
||||||
|
|
||||||
{driver, basho_bench_driver_mnesia_rocksdb}.
|
|
||||||
|
|
||||||
{key_generator, {int_to_bin,{uniform_int, 5000000}}}.
|
|
||||||
|
|
||||||
{value_generator, {fixed_bin, 10000}}.
|
|
||||||
|
|
||||||
{operations, [{get, 2}, {put, 2}, {delete, 1}]}.
|
|
||||||
|
|
||||||
{code_paths, ["/Users/uwiger/git/rocksdb",
|
|
||||||
"/Users/uwiger/git/mnesia_rocksdb"]}.
|
|
||||||
|
|
||||||
{mnesia_table, rdb}.
|
|
||||||
{backend, rocksdb_copies}.
|
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
-record(t, {k, i, v}).
|
-record(t, {k, i, v}).
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, run/1}).
|
||||||
run(Sz) ->
|
run(Sz) ->
|
||||||
mnesia:stop(),
|
mnesia:stop(),
|
||||||
init(),
|
init(),
|
||||||
|
|||||||
@@ -79,20 +79,20 @@ cleanup() ->
|
|||||||
os:cmd("rm *.BUP").
|
os:cmd("rm *.BUP").
|
||||||
|
|
||||||
mods(0) ->
|
mods(0) ->
|
||||||
[];
|
[].
|
||||||
mods(1) ->
|
%% mods(1) ->
|
||||||
[
|
%% [
|
||||||
{l, mnesia_rocksdb},
|
%% {l, mnesia_rocksdb},
|
||||||
{g, rocksdb}
|
%% {g, rocksdb}
|
||||||
];
|
%% ];
|
||||||
mods(2) ->
|
%% mods(2) ->
|
||||||
[
|
%% [
|
||||||
%% {l, mnesia_monitor},
|
%% %% {l, mnesia_monitor},
|
||||||
{g, mnesia_rocksdb},
|
%% {g, mnesia_rocksdb},
|
||||||
{l, mnesia_bup},
|
%% {l, mnesia_bup},
|
||||||
{g, mnesia_lib},
|
%% {g, mnesia_lib},
|
||||||
{g, mnesia_schema},
|
%% {g, mnesia_schema},
|
||||||
%% {g, mnesia_loader},
|
%% %% {g, mnesia_loader},
|
||||||
{g, mnesia_index},
|
%% {g, mnesia_index},
|
||||||
{l, mnesia_tm}
|
%% {l, mnesia_tm}
|
||||||
].
|
%% ].
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ end_per_testcase(_, _) ->
|
|||||||
%% ======================================================================
|
%% ======================================================================
|
||||||
|
|
||||||
cfg([Tab, Type, IxType], Config) ->
|
cfg([Tab, Type, IxType], Config) ->
|
||||||
[{my_config, #{tab => Tab, type => Type, ixtype => IxType}} | Config];
|
[{my_config, #{tab => Tab, type => Type, ixtype => IxType}} | Config].
|
||||||
cfg(Cfg, Config) when is_map(Cfg) -> [{my_config, Cfg} | Config].
|
%% cfg(Cfg, Config) when is_map(Cfg) -> [{my_config, Cfg} | Config].
|
||||||
|
|
||||||
cfg(Config) -> ?config(my_config, Config).
|
cfg(Config) -> ?config(my_config, Config).
|
||||||
|
|
||||||
|
|||||||
@@ -48,14 +48,18 @@
|
|||||||
-record(st, {}).
|
-record(st, {}).
|
||||||
-define(KEYS, [a,b,c]).
|
-define(KEYS, [a,b,c]).
|
||||||
|
|
||||||
|
-dialyzer({no_return, [basic_test_/0, test/1, prop_seq/0]}).
|
||||||
|
|
||||||
basic_test_() ->
|
basic_test_() ->
|
||||||
{timeout, 60000, [fun() -> test(100) end]}.
|
{timeout, 60000, [fun() -> test(100) end]}.
|
||||||
|
|
||||||
|
-dialyzer({no_opaque, test/1}).
|
||||||
test(N) ->
|
test(N) ->
|
||||||
setup_mnesia(),
|
setup_mnesia(),
|
||||||
true = proper:quickcheck(?MODULE:prop_seq(), N),
|
true = proper:quickcheck(?MODULE:prop_seq(), N),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
-dialyzer({no_opaque, prop_seq/0}).
|
||||||
prop_seq() ->
|
prop_seq() ->
|
||||||
?FORALL(Cmds, proper_statem:commands(?MODULE),
|
?FORALL(Cmds, proper_statem:commands(?MODULE),
|
||||||
begin
|
begin
|
||||||
|
|||||||
@@ -260,10 +260,10 @@ plain_transform1(Fun, [F|Fs]) when is_atom(element(1,F)) ->
|
|||||||
continue ->
|
continue ->
|
||||||
[list_to_tuple(plain_transform1(Fun, tuple_to_list(F))) |
|
[list_to_tuple(plain_transform1(Fun, tuple_to_list(F))) |
|
||||||
plain_transform1(Fun, Fs)];
|
plain_transform1(Fun, Fs)];
|
||||||
{done, NewF} ->
|
%% {done, NewF} ->
|
||||||
[NewF | Fs];
|
%% [NewF | Fs];
|
||||||
{error, Reason} ->
|
%% {error, Reason} ->
|
||||||
io:format("Error: ~p (~p)~n", [F,Reason]);
|
%% io:format("Error: ~p (~p)~n", [F,Reason]);
|
||||||
NewF when is_tuple(NewF) ->
|
NewF when is_tuple(NewF) ->
|
||||||
[NewF | plain_transform1(Fun, Fs)]
|
[NewF | plain_transform1(Fun, Fs)]
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ mk_tab(G) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
tab_name(fold ) -> r;
|
tab_name(fold ) -> r;
|
||||||
tab_name(select ) -> r;
|
%% tab_name(select ) -> r;
|
||||||
tab_name(rdb_fold) -> t.
|
tab_name(rdb_fold) -> t.
|
||||||
|
|
||||||
create_tab(T) ->
|
create_tab(T) ->
|
||||||
|
|||||||
Reference in New Issue
Block a user