diff options
Diffstat (limited to 'lib/mnesia/test/mnesia_schema_recovery_test.erl')
-rw-r--r-- | lib/mnesia/test/mnesia_schema_recovery_test.erl | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/lib/mnesia/test/mnesia_schema_recovery_test.erl b/lib/mnesia/test/mnesia_schema_recovery_test.erl new file mode 100644 index 0000000000..387238ae6b --- /dev/null +++ b/lib/mnesia/test/mnesia_schema_recovery_test.erl @@ -0,0 +1,787 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(mnesia_schema_recovery_test). +-author('[email protected]'). +-compile([export_all]). +-include("mnesia_test_lib.hrl"). + +init_per_testcase(Func, Conf) -> + mnesia_test_lib:init_per_testcase(Func, Conf). + +fin_per_testcase(Func, Conf) -> + mnesia_test_lib:fin_per_testcase(Func, Conf). + +-define(receive_messages(Msgs), receive_messages(Msgs, ?FILE, ?LINE)). + +% First Some debug logging +-define(dgb, true). +-ifdef(dgb). +-define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)). +-else. +-define(dl(X, Y), ok). +-endif. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all(doc) -> + ["Verify recoverabiliy of schema transactions.", + " Verify that a schema transaction", + " can be completed when it has been logged correctly and Mnesia", + " crashed before the log has been dumped. Then the transaction ", + " should be handled during the log dump at startup" + ]; +all(suite) -> + [interrupted_before_log_dump, + interrupted_after_log_dump]. + +interrupted_before_log_dump(suite) -> + [interrupted_before_create_ram, + interrupted_before_create_disc, + interrupted_before_create_disc_only, + interrupted_before_create_nostore, + interrupted_before_delete_ram, + interrupted_before_delete_disc, + interrupted_before_delete_disc_only, + interrupted_before_add_ram, + interrupted_before_add_disc, + interrupted_before_add_disc_only, + interrupted_before_add_kill_copier, + interrupted_before_move_ram, + interrupted_before_move_disc, + interrupted_before_move_disc_only, + interrupted_before_move_kill_copier, + interrupted_before_delcopy_ram, + interrupted_before_delcopy_disc, + interrupted_before_delcopy_disc_only, + interrupted_before_delcopy_kill_copier, + interrupted_before_addindex_ram, + interrupted_before_addindex_disc, + interrupted_before_addindex_disc_only, + interrupted_before_delindex_ram, + interrupted_before_delindex_disc, + interrupted_before_delindex_disc_only, + interrupted_before_change_type_ram2disc, + interrupted_before_change_type_ram2disc_only, + interrupted_before_change_type_disc2ram, + interrupted_before_change_type_disc2disc_only, + interrupted_before_change_type_disc_only2ram, + interrupted_before_change_type_disc_only2disc, + interrupted_before_change_type_other_node, + interrupted_before_change_schema_type %% Change schema table copy type!! + ]. + +interrupted_after_log_dump(suite) -> + [interrupted_after_create_ram, + interrupted_after_create_disc, + interrupted_after_create_disc_only, + interrupted_after_create_nostore, + interrupted_after_delete_ram, + interrupted_after_delete_disc, + interrupted_after_delete_disc_only, + interrupted_after_add_ram, + interrupted_after_add_disc, + interrupted_after_add_disc_only, + interrupted_after_add_kill_copier, + interrupted_after_move_ram, + interrupted_after_move_disc, + interrupted_after_move_disc_only, + interrupted_after_move_kill_copier, + interrupted_after_delcopy_ram, + interrupted_after_delcopy_disc, + interrupted_after_delcopy_disc_only, + interrupted_after_delcopy_kill_copier, + interrupted_after_addindex_ram, + interrupted_after_addindex_disc, + interrupted_after_addindex_disc_only, + interrupted_after_delindex_ram, + interrupted_after_delindex_disc, + interrupted_after_delindex_disc_only, + interrupted_after_change_type_ram2disc, + interrupted_after_change_type_ram2disc_only, + interrupted_after_change_type_disc2ram, + interrupted_after_change_type_disc2disc_only, + interrupted_after_change_type_disc_only2ram, + interrupted_after_change_type_disc_only2disc, + interrupted_after_change_type_other_node, + interrupted_after_change_schema_type %% Change schema table copy type!! + +% interrupted_before_change_access_mode, +% interrupted_before_transform, +% interrupted_before_restore, + ]. + +interrupted_before_create_ram(suite) -> []; +interrupted_before_create_ram(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, dump_schema_op}, + interrupted_create(Config, ram_copies, all, KillAt). + +interrupted_before_create_disc(suite) -> []; +interrupted_before_create_disc(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, dump_schema_op}, + interrupted_create(Config, disc_copies, all, KillAt). + +interrupted_before_create_disc_only(suite) -> []; +interrupted_before_create_disc_only(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, dump_schema_op}, + interrupted_create(Config, disc_only_copies, all, KillAt). + +interrupted_before_create_nostore(suite) -> []; +interrupted_before_create_nostore(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, dump_schema_op}, + interrupted_create(Config, ram_copies, one, KillAt). + +interrupted_after_create_ram(suite) -> []; +interrupted_after_create_ram(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, post_dump}, + interrupted_create(Config, ram_copies, all, KillAt). + +interrupted_after_create_disc(suite) -> []; +interrupted_after_create_disc(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, post_dump}, + interrupted_create(Config, disc_copies, all, KillAt). + +interrupted_after_create_disc_only(suite) -> []; +interrupted_after_create_disc_only(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, post_dump}, + interrupted_create(Config, disc_only_copies, all, KillAt). + +interrupted_after_create_nostore(suite) -> []; +interrupted_after_create_nostore(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, post_dump}, + interrupted_create(Config, ram_copies, one, KillAt). + +%%% After dump don't need debug point +interrupted_create(Config, Type, _Where, {mnesia_dumper, post_dump}) -> + [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]), + ?match({atomic, ok},mnesia:create_table(itrpt, [{Type, Nodes}])), + ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + case Type of + ram_copies -> + ?match([], mnesia:dirty_read({itrpt, before})); + _ -> + ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before})) + end, + ?verify_mnesia(Nodes, []); +interrupted_create(Config, Type, Where, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + {success, [A]} = ?start_activities([Node2]), + setup_dbgpoint(KillAt, Node2), + + if %% CREATE TABLE + Where == all -> % tables on both nodes + A ! fun() -> mnesia:create_table(itrpt, [{Type, Nodes}]) end; + true -> % no table on the killed node + A ! fun() -> mnesia:create_table(itrpt, [{Type, [Node1]}]) end + end, + + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Node2], [itrpt])), + %% Verify + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + verify_tab(Node1, Node2), + ?verify_mnesia(Nodes, []). + +interrupted_before_delete_ram(suite) -> []; +interrupted_before_delete_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delete(Config, ram_copies, Debug_Point). +interrupted_before_delete_disc(suite) -> []; +interrupted_before_delete_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delete(Config, disc_copies, Debug_Point). +interrupted_before_delete_disc_only(suite) -> []; +interrupted_before_delete_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delete(Config, disc_only_copies, Debug_Point). + +interrupted_after_delete_ram(suite) -> []; +interrupted_after_delete_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delete(Config, ram_copies, Debug_Point). +interrupted_after_delete_disc(suite) -> []; +interrupted_after_delete_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delete(Config, disc_copies, Debug_Point). +interrupted_after_delete_disc_only(suite) -> []; +interrupted_after_delete_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delete(Config, disc_only_copies, Debug_Point). + +interrupted_delete(Config, Type, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node2]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + {_Alive, Kill} = {Node1, Node2}, + {success, [A]} = ?start_activities([Kill]), + + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:delete_table(Tab) end, + + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Node2], [])), + Bad = {badrpc, {'EXIT', {aborted,{no_exists, Tab, all}}}}, + ?match(Bad, rpc:call(Node1, mnesia, table_info, [Tab, all])), + ?match(Bad, rpc:call(Node2, mnesia, table_info, [Tab, all])), + ?verify_mnesia(Nodes, []). + +interrupted_before_add_ram(suite) -> []; +interrupted_before_add_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_add(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_before_add_disc(suite) -> []; +interrupted_before_add_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_add(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_before_add_disc_only(suite) -> []; +interrupted_before_add_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_before_add_kill_copier(suite) -> []; +interrupted_before_add_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_add(Config, ram_copies, kill_copier, Debug_Point). + +interrupted_after_add_ram(suite) -> []; +interrupted_after_add_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_add(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_after_add_disc(suite) -> []; +interrupted_after_add_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_add(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_after_add_disc_only(suite) -> []; +interrupted_after_add_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_after_add_kill_copier(suite) -> []; +interrupted_after_add_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_add(Config, ram_copies, kill_copier, Debug_Point). + +%%% After dump don't need debug point +interrupted_add(Config, Type, _Where, {mnesia_dumper, post_dump}) -> + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node2]}, {local_content,true}])), + ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node1, Type)), + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + case Type of + ram_copies -> + ?match([], mnesia:dirty_read({itrpt, before})); + _ -> + ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before})) + end, + ?verify_mnesia(Nodes, []); +interrupted_add(Config, Type, Who, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + {_Alive, Kill} = + if Who == kill_reciever -> + {Node1, Node2}; + true -> + {Node2, Node1} + end, + {success, [A]} = ?start_activities([Kill]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + + setup_dbgpoint(KillAt, Kill), + + A ! fun() -> mnesia:add_table_copy(Tab, Node2, Type) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])), + verify_tab(Node1, Node2), + ?verify_mnesia(Nodes, []). + +interrupted_before_move_ram(suite) -> []; +interrupted_before_move_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_move(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_before_move_disc(suite) -> []; +interrupted_before_move_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_move(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_before_move_disc_only(suite) -> []; +interrupted_before_move_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_before_move_kill_copier(suite) -> []; +interrupted_before_move_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_move(Config, ram_copies, kill_copier, Debug_Point). + +interrupted_after_move_ram(suite) -> []; +interrupted_after_move_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_move(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_after_move_disc(suite) -> []; +interrupted_after_move_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_move(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_after_move_disc_only(suite) -> []; +interrupted_after_move_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_after_move_kill_copier(suite) -> []; +interrupted_after_move_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_move(Config, ram_copies, kill_copier, Debug_Point). + +%%% After dump don't need debug point +interrupted_move(Config, Type, _Where, {mnesia_dumper, post_dump}) -> + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])), + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)), + ?match(ok, mnesia:dirty_write({itrpt, aFter, 1})), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before})), + ?match([{itrpt, aFter, 1}], mnesia:dirty_read({itrpt, aFter})), + ?verify_mnesia(Nodes, []); +interrupted_move(Config, Type, Who, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + + {_Alive, Kill} = + if Who == kill_reciever -> + if Type == ram_copies -> + {atomic, ok} = mnesia:dump_tables([Tab]); + true -> + ignore + end, + {Node1, Node2}; + true -> + {Node2, Node1} + end, + + {success, [A]} = ?start_activities([Kill]), + + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:move_table_copy(Tab, Node1, Node2) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])), + verify_tab(Node1, Node2), + ?verify_mnesia(Nodes, []). + +interrupted_before_delcopy_ram(suite) -> []; +interrupted_before_delcopy_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delcopy(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_before_delcopy_disc(suite) -> []; +interrupted_before_delcopy_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_before_delcopy_disc_only(suite) -> []; +interrupted_before_delcopy_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_before_delcopy_kill_copier(suite) -> []; +interrupted_before_delcopy_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delcopy(Config, ram_copies, kill_copier, Debug_Point). + +interrupted_after_delcopy_ram(suite) -> []; +interrupted_after_delcopy_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delcopy(Config, ram_copies, kill_reciever, Debug_Point). +interrupted_after_delcopy_disc(suite) -> []; +interrupted_after_delcopy_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point). +interrupted_after_delcopy_disc_only(suite) -> []; +interrupted_after_delcopy_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point). +interrupted_after_delcopy_kill_copier(suite) -> []; +interrupted_after_delcopy_kill_copier(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delcopy(Config, ram_copies, kill_copier, Debug_Point). + + +%%% After dump don't need debug point +interrupted_delcopy(Config, Type, _Where, {mnesia_dumper, post_dump}) -> + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1,Node2]}])), + ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node1)), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + ?match([Node2], mnesia:table_info(itrpt,Type)), + ?verify_mnesia(Nodes, []); +interrupted_delcopy(Config, Type, Who, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = + ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1, Node2]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + + {_Alive, Kill} = + if Who == kill_reciever -> + {Node1, Node2}; + true -> + if + Type == ram_copies -> + {atomic, ok} = mnesia:dump_tables([Tab]); + true -> + ignore + end, + {Node2, Node1} + end, + + {success, [A]} = ?start_activities([Kill]), + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:del_table_copy(Tab, Node2) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])), + verify_tab(Node1, Node2), + ?verify_mnesia(Nodes, []). + +interrupted_before_addindex_ram(suite) -> []; +interrupted_before_addindex_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_addindex(Config, ram_copies, Debug_Point). +interrupted_before_addindex_disc(suite) -> []; +interrupted_before_addindex_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_addindex(Config, disc_copies, Debug_Point). +interrupted_before_addindex_disc_only(suite) -> []; +interrupted_before_addindex_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_addindex(Config, disc_only_copies, Debug_Point). + +interrupted_after_addindex_ram(suite) -> []; +interrupted_after_addindex_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_addindex(Config, ram_copies, Debug_Point). +interrupted_after_addindex_disc(suite) -> []; +interrupted_after_addindex_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_addindex(Config, disc_copies, Debug_Point). +interrupted_after_addindex_disc_only(suite) -> []; +interrupted_after_addindex_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_addindex(Config, disc_only_copies, Debug_Point). + + +%%% After dump don't need debug point +interrupted_addindex(Config, Type, {mnesia_dumper, post_dump}) -> + [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic,ok},mnesia:create_table(Tab, [{Type, Nodes}])), + ?match({atomic,ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match({atomic,ok}, mnesia:add_table_index(Tab, val)), + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + case Type of + ram_copies -> + ?match([], mnesia:dirty_index_read(itrpt, 1, val)); + _ -> + ?match([{itrpt, before, 1}], mnesia:dirty_index_read(itrpt, 1, val)) + end, + ?verify_mnesia(Nodes, []); +interrupted_addindex(Config, Type, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + {_Alive, Kill} = {Node1, Node2}, + {success, [A]} = ?start_activities([Kill]), + + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:add_table_index(Tab, val) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Node2], [])), + + verify_tab(Node1, Node2), + ?match([{Tab, b, a}, {Tab, a, a}], + rpc:call(Node1, mnesia, dirty_index_read, [itrpt, a, val])), + ?match([{Tab, b, a}, {Tab, a, a}], + rpc:call(Node2, mnesia, dirty_index_read, [itrpt, a, val])), + ?verify_mnesia(Nodes, []). + +interrupted_before_delindex_ram(suite) -> []; +interrupted_before_delindex_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delindex(Config, ram_copies, Debug_Point). +interrupted_before_delindex_disc(suite) -> []; +interrupted_before_delindex_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delindex(Config, disc_copies, Debug_Point). +interrupted_before_delindex_disc_only(suite) -> []; +interrupted_before_delindex_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_delindex(Config, disc_only_copies, Debug_Point). + +interrupted_after_delindex_ram(suite) -> []; +interrupted_after_delindex_ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delindex(Config, ram_copies, Debug_Point). +interrupted_after_delindex_disc(suite) -> []; +interrupted_after_delindex_disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delindex(Config, disc_copies, Debug_Point). +interrupted_after_delindex_disc_only(suite) -> []; +interrupted_after_delindex_disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_delindex(Config, disc_only_copies, Debug_Point). + +%%% After dump don't need debug point +interrupted_delindex(Config, Type, {mnesia_dumper, post_dump}) -> + [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic,ok},mnesia:create_table(Tab, [{Type, Nodes},{index,[val]}])), + ?match({atomic,ok},mnesia:create_table(test, [{disc_copies,[Node1]}])), + ?match({atomic,ok}, mnesia:del_table_index(Tab, val)), + ?match(ok, mnesia:dirty_write({itrpt, before, 1})), + ?match(ok, mnesia:dirty_write({test, found_in_log, 1})), + ?match(stopped, mnesia:stop()), + ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])), + %% Verify + ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})), + ?match({'EXIT',{aborted,{badarg,_}}}, mnesia:dirty_index_read(itrpt, 1, val)), + ?verify_mnesia(Nodes, []); + +interrupted_delindex(Config, Type, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{index, [val]}, + {Type, [Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + {_Alive, Kill} = {Node1, Node2}, + {success, [A]} = ?start_activities([Kill]), + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:del_table_index(Tab, val) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia([Node2], [])), + verify_tab(Node1, Node2), + ?match({badrpc, _}, rpc:call(Node1, mnesia, dirty_index_read, [itrpt, a, val])), + ?match({badrpc, _}, rpc:call(Node2, mnesia, dirty_index_read, [itrpt, a, val])), + ?match([], rpc:call(Node1, mnesia, table_info, [Tab, index])), + ?match([], rpc:call(Node2, mnesia, table_info, [Tab, index])), + ?verify_mnesia(Nodes, []). + +interrupted_before_change_type_ram2disc(suite) -> []; +interrupted_before_change_type_ram2disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point). +interrupted_before_change_type_ram2disc_only(suite) -> []; +interrupted_before_change_type_ram2disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point). +interrupted_before_change_type_disc2ram(suite) -> []; +interrupted_before_change_type_disc2ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point). +interrupted_before_change_type_disc2disc_only(suite) -> []; +interrupted_before_change_type_disc2disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point). +interrupted_before_change_type_disc_only2ram(suite) -> []; +interrupted_before_change_type_disc_only2ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point). +interrupted_before_change_type_disc_only2disc(suite) -> []; +interrupted_before_change_type_disc_only2disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point). +interrupted_before_change_type_other_node(suite) -> []; +interrupted_before_change_type_other_node(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, dump_schema_op}, + interrupted_change_type(Config, ram_copies, disc_copies, the_other_one, Debug_Point). + +interrupted_after_change_type_ram2disc(suite) -> []; +interrupted_after_change_type_ram2disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point). +interrupted_after_change_type_ram2disc_only(suite) -> []; +interrupted_after_change_type_ram2disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point). +interrupted_after_change_type_disc2ram(suite) -> []; +interrupted_after_change_type_disc2ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point). +interrupted_after_change_type_disc2disc_only(suite) -> []; +interrupted_after_change_type_disc2disc_only(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point). +interrupted_after_change_type_disc_only2ram(suite) -> []; +interrupted_after_change_type_disc_only2ram(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point). +interrupted_after_change_type_disc_only2disc(suite) -> []; +interrupted_after_change_type_disc_only2disc(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point). +interrupted_after_change_type_other_node(suite) -> []; +interrupted_after_change_type_other_node(Config) when is_list(Config) -> + Debug_Point = {mnesia_dumper, post_dump}, + interrupted_change_type(Config, ram_copies, disc_copies, the_other_one, Debug_Point). + +interrupted_change_type(Config, FromType, ToType, Who, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{FromType, [Node2, Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + + {_Alive, Kill} = + if Who == changer -> {Node1, Node2}; + true -> {Node2, Node1} + end, + + {success, [A]} = ?start_activities([Kill]), + setup_dbgpoint(KillAt, Kill), + A ! fun() -> mnesia:change_table_copy_type(Tab, Node2, ToType) end, + kill_at_debug(), + ?match([], mnesia_test_lib:start_mnesia(Nodes, [itrpt])), + verify_tab(Node1, Node2), + ?match(FromType, rpc:call(Node1, mnesia, table_info, [Tab, storage_type])), + ?match(ToType, rpc:call(Node2, mnesia, table_info, [Tab, storage_type])), + ?verify_mnesia(Nodes, []). + +interrupted_before_change_schema_type(suite) -> []; +interrupted_before_change_schema_type(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, dump_schema_op}, + interrupted_change_schema_type(Config, KillAt). + +interrupted_after_change_schema_type(suite) -> []; +interrupted_after_change_schema_type(Config) when is_list(Config) -> + KillAt = {mnesia_dumper, post_dump}, + interrupted_change_schema_type(Config, KillAt). + +-define(cleanup(N, Config), + mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}], + N, Config, ?FILE, ?LINE)). + +interrupted_change_schema_type(Config, KillAt) -> + ?is_debug_compiled, + [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]), + + Tab = itrpt, + ?match({atomic, ok}, mnesia:create_table(Tab, [{ram_copies, [Node2, Node1]}])), + ?match(ok, mnesia:dirty_write({Tab, before, 1})), + + {success, [A]} = ?start_activities([Node2]), + setup_dbgpoint(KillAt, Node2), + + A ! fun() -> mnesia:change_table_copy_type(schema, Node2, ram_copies) end, + kill_at_debug(), + ?match(ok, rpc:call(Node2, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])), + ?match(ok, rpc:call(Node2, mnesia, wait_for_tables, [[itrpt, schema], 2000])), + ?match(disc_copies, rpc:call(Node1, mnesia, table_info, [schema, storage_type])), + ?match(ram_copies, rpc:call(Node2, mnesia, table_info, [schema, storage_type])), + + %% Go back to disc_copies !! + {success, [B]} = ?start_activities([Node2]), + setup_dbgpoint(KillAt, Node2), + B ! fun() -> mnesia:change_table_copy_type(schema, Node2, disc_copies) end, + kill_at_debug(), + + ?match(ok, rpc:call(Node2, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])), + ?match(ok, rpc:call(Node2, mnesia, wait_for_tables, [[itrpt, schema], 2000])), + ?match(disc_copies, rpc:call(Node1, mnesia, table_info, [schema, storage_type])), + ?match(disc_copies, rpc:call(Node2, mnesia, table_info, [schema, storage_type])), + + ?verify_mnesia(Nodes, []), + ?cleanup(2, Config). + +%%% Helpers +verify_tab(Node1, Node2) -> + ?match({atomic, ok}, + rpc:call(Node1, mnesia, transaction, [fun() -> mnesia:dirty_write({itrpt, a, a}) end])), + ?match({atomic, ok}, + rpc:call(Node2, mnesia, transaction, [fun() -> mnesia:dirty_write({itrpt, b, a}) end])), + ?match([{itrpt,a,a}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, a}])), + ?match([{itrpt,a,a}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, a}])), + ?match([{itrpt,b,a}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, b}])), + ?match([{itrpt,b,a}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, b}])), + ?match([{itrpt,before,1}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, before}])), + ?match([{itrpt,before,1}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, before}])). + +setup_dbgpoint(DbgPoint, Where) -> + Self = self(), + TestFun = fun(_, [InitBy]) -> + case InitBy of + schema_prepare -> + ignore; + schema_begin -> + ignore; + _Other -> + ?deactivate_debug_fun(DbgPoint), + unlink(Self), + Self ! {fun_done, node()}, + timer:sleep(infinity) + end + end, + %% Kill when debug has been reached + ?remote_activate_debug_fun(Where, DbgPoint, TestFun, []). + +kill_at_debug() -> + %% Wait till it's killed + receive + {fun_done, Node} -> + ?match([], mnesia_test_lib:kill_mnesia([Node])) + after + timer:minutes(1) -> ?error("Timeout in kill_at_debug", []) + end. + |