mnesia_rocksdb/test/mnesia_rocksdb_migration_SUITE.erl

192 lines
5.5 KiB
Erlang

-module(mnesia_rocksdb_migration_SUITE).
-export([
all/0
, suite/0
, groups/0
, init_per_suite/1
, end_per_suite/1
, init_per_group/2
, end_per_group/2
, init_per_testcase/2
, end_per_testcase/2
]).
-export([
manual_migration/1
, migrate_with_encoding_change/1
, auto_migration/1
]).
-include_lib("common_test/include/ct.hrl").
-define(TABS_CREATED, tables_created).
suite() ->
[].
all() ->
[{group, all_tests}].
groups() ->
[
{all_tests, [sequence], [ manual_migration
, migrate_with_encoding_change ]}
].
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_, Config) ->
Config.
end_per_group(_, _Config) ->
ok.
init_per_testcase(_, Config) ->
mnesia:stop(),
ok = mnesia_rocksdb_tlib:start_mnesia(reset),
Config.
%% create_migrateable_db(Config).
end_per_testcase(_, _Config) ->
ok.
manual_migration(Config) ->
tr_ct:with_trace(fun manual_migration_/1, Config, tr_opts()).
manual_migration_(Config) ->
create_migrateable_db(Config),
Tabs = tables(),
ct:log("Analyze (before): ~p", [analyze_tabs(Tabs)]),
Res = mnesia_rocksdb_admin:migrate_standalone(rdb, Tabs),
ct:log("migrate_standalone(rdb, ~p) -> ~p", [Tabs, Res]),
AnalyzeRes = analyze_tabs(Tabs),
ct:log("AnalyzeRes = ~p", [AnalyzeRes]),
MigRes = mnesia_rocksdb_admin:migrate_standalone(rdb, Tabs),
ct:log("MigRes = ~p", [MigRes]),
AnalyzeRes2 = analyze_tabs(Tabs),
ct:log("AnalyzeRes2 = ~p", [AnalyzeRes2]),
ct:log("Admin State = ~p", [sys:get_state(mnesia_rocksdb_admin)]),
ok.
migrate_with_encoding_change(_Config) ->
ok = create_tab(t, [{user_properties, [{mrdb_encoding, {sext,{object,term}}},
{rocksdb_standalone, true}]},
{index,[val]}
]),
mrdb:insert(t, {t, <<"1">>, <<"a">>}),
mrdb:insert(t, {t, <<"2">>, <<"b">>}),
TRef = mrdb:get_ref(t),
{ok, V1} = mrdb:rdb_get(TRef, sext:encode(<<"1">>), []),
{ok, V2} = mrdb:rdb_get(TRef, sext:encode(<<"2">>), []),
{t,[],<<"a">>} = binary_to_term(V1),
{t,[],<<"b">>} = binary_to_term(V2),
Opts = #{encoding => {raw, raw}},
MigRes = mnesia_rocksdb_admin:migrate_standalone(rdb, [{t, Opts}]),
ct:log("MigRes (t) = ~p", [MigRes]),
%%
%% Ensure that metadata reflect the migrated table
%% (now a column family, and the rocksdb_standalone prop gone)
%%
TRef1 = mrdb:get_ref(t),
ct:log("TRef1(t) = ~p", [TRef1]),
#{type := column_family,
properties := #{user_properties := UPs}} = TRef1,
error = maps:find(rocksdb_standalone, UPs),
UPsR = lists:sort(maps:values(UPs)),
UPsM = lists:sort(mnesia:table_info(t, user_properties)),
{UPsR,UPsM} = {UPsM,UPsR},
ct:log("user properties (t): ~p", [UPsM]),
[{<<"2">>, <<"b">>},
{<<"1">>, <<"a">>}] = mrdb:rdb_fold(
t, fun(K,V,A) -> [{K,V}|A] end, [], <<>>),
ct:log("All data present in new column family", []),
ct:log("Contents of mnesia dir: ~p",
[ok(file:list_dir(mnesia:system_info(directory)))]),
ct:log("mnesia stopped", []),
mnesia:stop(),
mnesia:start(),
ct:log("mnesia started", []),
mnesia:info(),
ok = mnesia:wait_for_tables([t], 3000),
ct:log("tables loaded", []),
[{t,<<"1">>,<<"a">>},
{t,<<"2">>,<<"b">>}] = mrdb:select(
t, [{'_',[],['$_']}]),
[{<<"2">>,<<"b">>},
{<<"1">>,<<"a">>}] = mrdb:rdb_fold(
t, fun(K,V,A) -> [{K,V}|A] end, [], <<>>),
ok.
auto_migration(_Config) ->
ok.
ok({ok, Value}) -> Value.
tr_opts() ->
#{ patterns => [ {mnesia_rocksdb_admin, '_', []}
, {mnesia_rocksdb_lib, '_', []}
, {rocksdb, '_', x} | trace_exports(mrdb, x) ] }.
trace_exports(M, Pat) ->
Fs = M:module_info(exports),
[{M, F, A, Pat} || {F, A} <- Fs].
tables() ->
[a].
create_migrateable_db(Config) ->
Os = [{user_properties, [{rocksdb_standalone, true}]}],
TabNames = tables(),
Tabs = [{T, Os} || T <- TabNames],
create_tabs(Tabs, Config),
verify_tabs_are_standalone(TabNames),
fill_tabs(TabNames),
Config.
fill_tabs(Tabs) ->
%% Fill with more than 300, since that's the currently hard-coded chunk size
lists:foreach(fun(Tab) ->
[mrdb:insert(Tab, {Tab, X, a}) || X <- lists:seq(1,500)]
end, Tabs).
create_tabs(Tabs, Config) ->
Res = lists:map(fun create_tab/1, Tabs),
tr_ct:trace_checkpoint(?TABS_CREATED, Config),
Res.
create_tab({T, Opts}) ->
create_tab(T, Opts).
create_tab(T, Opts) ->
{atomic, ok} = mnesia:create_table(T, [{rdb, [node()]} | Opts]),
ok.
verify_tabs_are_standalone(Tabs) ->
case analyze_tabs(Tabs) of
{_, []} ->
ok;
{[], NotSA} ->
error({not_standalone, NotSA})
end.
analyze_tabs(Tabs) ->
Dir = mnesia:system_info(directory),
Files = filelib:wildcard(filename:join(Dir, "*-_tab.extrdb")),
ct:log("Files = ~p", [Files]),
TabNames = lists:map(
fun(F) ->
{match,[TStr]} =
re:run(F, "^.+/([^/]+)-_tab\\.extrdb$",
[{capture, [1], list}]),
list_to_existing_atom(TStr)
end, Files),
ct:log("TabNames = ~p", [TabNames]),
NotSA = Tabs -- TabNames,
{TabNames -- NotSA, NotSA}.