WIP migrate table encoding
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
manual_migration/1
|
||||
, migrate_with_encoding_change/1
|
||||
, auto_migration/1
|
||||
, online_encoding_migration/1
|
||||
, online_encoding_migration_restart/1
|
||||
, online_encoding_migration_interrupt/1
|
||||
]).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
@@ -31,7 +34,10 @@ all() ->
|
||||
groups() ->
|
||||
[
|
||||
{all_tests, [sequence], [ manual_migration
|
||||
, migrate_with_encoding_change ]}
|
||||
, migrate_with_encoding_change
|
||||
, online_encoding_migration
|
||||
, online_encoding_migration_restart
|
||||
, online_encoding_migration_interrupt ]}
|
||||
].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
@@ -125,6 +131,113 @@ migrate_with_encoding_change(_Config) ->
|
||||
auto_migration(_Config) ->
|
||||
ok.
|
||||
|
||||
%% Online encoding migration: term keys -> sext keys while dual-writing.
|
||||
online_encoding_migration(_Config) ->
|
||||
ok = create_tab(enc, [{attributes, [k, v]}]),
|
||||
%% Default set encoding is {term, {value, term}} for 2-attr tables.
|
||||
lists:foreach(
|
||||
fun(I) ->
|
||||
mrdb:insert(enc, {enc, I, I * 10})
|
||||
end, lists:seq(1, 50)),
|
||||
50 = length(mrdb:select(enc, [{'_', [], ['$_']}])),
|
||||
Ref0 = mrdb:get_ref(enc),
|
||||
ct:log("Before migration ref: ~p", [maps:with([encoding, type, semantics], Ref0)]),
|
||||
{term, _} = maps:get(encoding, Ref0),
|
||||
%% Concurrent writes during migration
|
||||
ok = mnesia_rocksdb_admin:migrate_encoding(
|
||||
rdb, enc, {sext, {value, term}}),
|
||||
%% Dual-write should be active
|
||||
#{migration := _} = mrdb:get_ref(enc),
|
||||
mrdb:insert(enc, {enc, 100, 1000}),
|
||||
mrdb:delete(enc, 5),
|
||||
%% Wait for copy_done
|
||||
ok = wait_copy_done(enc, 50),
|
||||
ct:log("Status after copy: ~p", [mnesia_rocksdb_admin:migration_status(enc)]),
|
||||
ok = mnesia_rocksdb_admin:finalize_migration(rdb, enc),
|
||||
Ref1 = mrdb:get_ref(enc),
|
||||
ct:log("After finalize ref: ~p", [maps:with([encoding, type], Ref1)]),
|
||||
{sext, _} = maps:get(encoding, Ref1),
|
||||
false = maps:is_key(migration, Ref1),
|
||||
Objs = lists:sort(mrdb:select(enc, [{'_', [], ['$_']}])),
|
||||
%% 50 - 1 deleted + 1 inserted = 50
|
||||
50 = length(Objs),
|
||||
false = lists:keymember(5, 2, Objs),
|
||||
true = lists:keymember(100, 2, Objs),
|
||||
{enc, 100, 1000} = lists:keyfind(100, 2, Objs),
|
||||
%% sext-encoded key is readable via rdb_get
|
||||
{ok, _} = mrdb:rdb_get(Ref1, sext:encode(100), []),
|
||||
ok.
|
||||
|
||||
wait_copy_done(_Tab, 0) ->
|
||||
{error, timeout};
|
||||
wait_copy_done(Tab, N) ->
|
||||
case mnesia_rocksdb_admin:migration_status(Tab) of
|
||||
#{phase := copy_done} ->
|
||||
ok;
|
||||
_ ->
|
||||
timer:sleep(50),
|
||||
wait_copy_done(Tab, N - 1)
|
||||
end.
|
||||
|
||||
%% Complete migration, restart mnesia, verify encoding + data survive.
|
||||
online_encoding_migration_restart(_Config) ->
|
||||
ok = create_tab(enc_r, [{attributes, [k, v]}]),
|
||||
[mrdb:insert(enc_r, {enc_r, I, I * 3}) || I <- lists:seq(1, 30)],
|
||||
ok = mnesia_rocksdb_admin:migrate_encoding(
|
||||
rdb, enc_r, {sext, {value, term}}),
|
||||
ok = wait_copy_done(enc_r, 100),
|
||||
ok = mnesia_rocksdb_admin:finalize_migration(rdb, enc_r),
|
||||
{sext, _} = maps:get(encoding, mrdb:get_ref(enc_r)),
|
||||
Before = lists:sort(mrdb:select(enc_r, [{'_', [], ['$_']}])),
|
||||
30 = length(Before),
|
||||
%% Close and reopen DB (same VM: stop/start mnesia + wait for tables).
|
||||
stopped = mnesia:stop(),
|
||||
ok = mnesia:start(),
|
||||
ok = mnesia:wait_for_tables([enc_r], 10000),
|
||||
Ref = mrdb:get_ref(enc_r),
|
||||
ct:log("After restart ref: ~p",
|
||||
[maps:with([encoding, cf_gen, type], Ref)]),
|
||||
{sext, _} = maps:get(encoding, Ref),
|
||||
false = maps:is_key(migration, Ref),
|
||||
After = lists:sort(mrdb:select(enc_r, [{'_', [], ['$_']}])),
|
||||
Before = After,
|
||||
{ok, _} = mrdb:rdb_get(Ref, sext:encode(1), []),
|
||||
ok.
|
||||
|
||||
%% Stop mid-migration (after dual-write is up), reopen, finish copy + finalize.
|
||||
online_encoding_migration_interrupt(_Config) ->
|
||||
ok = create_tab(enc_i, [{attributes, [k, v]}]),
|
||||
[mrdb:insert(enc_i, {enc_i, I, I}) || I <- lists:seq(1, 100)],
|
||||
ok = mnesia_rocksdb_admin:migrate_encoding(
|
||||
rdb, enc_i, {sext, {value, term}}),
|
||||
%% Dual-write active; do not wait for copy_done — interrupt promptly.
|
||||
#{migration := _} = mrdb:get_ref(enc_i),
|
||||
mrdb:insert(enc_i, {enc_i, 200, 200}),
|
||||
mrdb:delete(enc_i, 10),
|
||||
stopped = mnesia:stop(),
|
||||
ok = mnesia:start(),
|
||||
ok = mnesia:wait_for_tables([enc_i], 10000),
|
||||
%% Migration should be re-armed (dual-write) or already copy_done.
|
||||
case mnesia_rocksdb_admin:migration_status(enc_i) of
|
||||
#{phase := Phase} when Phase =:= copying; Phase =:= copy_done ->
|
||||
ok;
|
||||
Other ->
|
||||
ct:fail({expected_migration_after_restart, Other})
|
||||
end,
|
||||
%% Writes during resumed dual-write
|
||||
mrdb:insert(enc_i, {enc_i, 201, 201}),
|
||||
ok = wait_copy_done(enc_i, 200),
|
||||
ok = mnesia_rocksdb_admin:finalize_migration(rdb, enc_i),
|
||||
{sext, _} = maps:get(encoding, mrdb:get_ref(enc_i)),
|
||||
false = maps:is_key(migration, mrdb:get_ref(enc_i)),
|
||||
Objs = lists:sort(mrdb:select(enc_i, [{'_', [], ['$_']}])),
|
||||
false = lists:keymember(10, 2, Objs),
|
||||
true = lists:keymember(200, 2, Objs),
|
||||
true = lists:keymember(201, 2, Objs),
|
||||
%% 100 - 1 delete + 2 inserts = 101
|
||||
101 = length(Objs),
|
||||
ok.
|
||||
|
||||
ok({ok, Value}) -> Value.
|
||||
|
||||
tr_opts() ->
|
||||
|
||||
Reference in New Issue
Block a user