1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1996-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%
   21: -module(mnesia_install_test).
   22: -author('hakan@erix.ericsson.se').
   23: 
   24: -compile([export_all]).
   25: -include("mnesia_test_lib.hrl").
   26: 
   27: init_per_testcase(Func, Conf) ->
   28:     mnesia_test_lib:init_per_testcase(Func, Conf).
   29: 
   30: end_per_testcase(Func, Conf) ->
   31:     mnesia_test_lib:end_per_testcase(Func, Conf).
   32: 
   33: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   34: all() -> 
   35:     [silly_durability, silly_move, silly_upgrade].
   36: 
   37: groups() -> 
   38:     [{stress, [], stress_cases()}].
   39: 
   40: init_per_group(_GroupName, Config) ->
   41:     Config.
   42: 
   43: end_per_group(_GroupName, Config) ->
   44:     Config.
   45: 
   46: 
   47: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   48: %% Stepwise of more and more advanced features
   49: silly() ->
   50:     Nodes = [node()] ++ nodes(),
   51:     mnesia_test_lib:kill_mnesia(Nodes),
   52:     Config = [{nodes, Nodes}],
   53:     mnesia_test_lib:eval_test_case(?MODULE, silly2, Config).
   54: 
   55: silly2(Config) when is_list(Config) ->
   56:     [Node1 | _] = Nodes = ?acquire_nodes(3, Config),
   57:     mnesia_test_lib:kill_mnesia(Nodes),
   58:     ?ignore([mnesia:delete_schema([N]) || N <- Nodes]),
   59:     ?match(ok, mnesia:create_schema([Node1])),
   60:     ?match(ok, rpc:call(Node1, mnesia, start, [])),
   61:     ?match(ok, rpc:call(Node1, mnesia, wait_for_tables,
   62: 			[[schema], infinity])),
   63:     Res = silly_durability(Config),
   64:     StressFun = fun(F) -> apply(?MODULE, F, [Config]) end,
   65:     R =
   66: 	case length(Nodes) of
   67: 	    L when L > 1 ->
   68: 		Node2 = lists:nth(2, Nodes),
   69: 		AddDb = [schema, Node2, ram_copies],
   70: 		?match({atomic, ok},
   71: 		       rpc:call(Node1, mnesia, add_table_copy, AddDb)),
   72: 		Args = [[{extra_db_nodes, [Node1]}]],
   73: 		?match(ok, rpc:call(Node2, mnesia, start, Args)),
   74: 		ChangeDb = [schema, Node2, disc_copies],
   75: 		?match({atomic, ok},
   76: 		       rpc:call(Node1, mnesia, change_table_copy_type,
   77: 				ChangeDb)),
   78: 		?match([], mnesia_test_lib:sync_tables([Node1, Node2],
   79: 						       [schema])),
   80: 		MoveRes = silly_move(Config),
   81: 		UpgradeRes = silly_upgrade(Config),
   82: 		StressRes = [StressFun(F) || F <- stress_cases()],
   83: 		?verify_mnesia([Node2], []),
   84: 		[Res, MoveRes, UpgradeRes] ++ StressRes;
   85: 	    _ ->
   86: 		StressRes = [StressFun(F) || F <- stress_cases()],
   87: 		?warning("Too few nodes. Perform net_adm:ping(OtherNode) "
   88: 			 "and rerun!!!~n", []),
   89: 		[Res | StressRes]
   90: 	end,
   91:     ?verify_mnesia([Node1], []),
   92:     R.
   93: 
   94: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   95: silly_durability(doc) ->
   96:     ["Simple test of durability"];
   97: silly_durability(suite) -> [];
   98: silly_durability(Config) when is_list(Config) ->
   99:     [Node1] = ?acquire_nodes(1, Config),
  100:     Tab = silly,
  101:     Storage = mnesia_test_lib:storage_type(disc_copies, Config),   
  102:     
  103:     ?match({atomic, ok}, rpc:call(Node1, mnesia, 
  104: 				  create_table, [Tab, [{Storage, [Node1]}]])),
  105: 
  106:     Read = fun() -> mnesia:read({Tab, a}) end,
  107:     Write = fun() -> mnesia:write({Tab, a, b}) end,
  108: 
  109:     ?match({atomic, []},
  110: 	   rpc:call(Node1, mnesia, transaction, [Read])),
  111:     ?match({atomic, ok},
  112: 	   rpc:call(Node1, mnesia, transaction, [Write])),
  113:     ?match({atomic, [{Tab, a, b}]}, 
  114: 	   rpc:call(Node1, mnesia, transaction, [Read])),
  115:     
  116:     ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
  117:     ?match(ok, rpc:call(Node1, mnesia, start, [])),
  118:     case mnesia_test_lib:diskless(Config) of
  119: 	true -> 
  120: 	    skip;
  121: 	false ->
  122: 	    ?match(ok, rpc:call(Node1, mnesia, wait_for_tables, [[Tab], infinity])),	    
  123: 	    ?match({atomic, [{Tab, a, b}]},
  124: 		   rpc:call(Node1, mnesia, transaction, [Read]))
  125:     end,
  126:     ?verify_mnesia([Node1], []).
  127: 
  128: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  129: silly_move(doc) ->
  130:     ["Simple test of movement of a replica from one node to another"];
  131: silly_move(suite) -> [];
  132: silly_move(Config) when is_list(Config) ->
  133:     [Node1, Node2] = ?acquire_nodes(2, Config),
  134:     Tab = silly_move,
  135:     ?match({atomic, ok},
  136: 	   rpc:call(Node1, mnesia,
  137: 		    create_table, [Tab, [{ram_copies, [Node2]}]])),
  138:     ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab])),
  139: 
  140:     Read = fun() -> mnesia:read({Tab, a}) end,
  141:     Write = fun() -> mnesia:write({Tab, a, b}) end,
  142: 
  143:     ?match({atomic, []},
  144: 	   rpc:call(Node1, mnesia, transaction, [Read])),
  145:     ?match({atomic, ok},
  146: 	   rpc:call(Node1, mnesia, transaction, [Write])),
  147:     ?match({atomic, [{Tab, a, b}]}, 
  148: 	   rpc:call(Node1, mnesia, transaction, [Read])),
  149:     
  150:     case mnesia_test_lib:diskless(Config) of
  151: 	true -> skip;
  152: 	false -> 
  153: 	    ?match({atomic, ok}, 
  154: 		   rpc:call(Node1, mnesia,
  155: 			    change_table_copy_type, [Tab, Node2, disc_only_copies])),
  156: 	    ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab]))
  157:     end,
  158:     ?match({atomic, [{Tab, a, b}]}, rpc:call(Node1, mnesia, transaction, [Read])),
  159: 
  160:     ?match({atomic, ok},
  161: 	   rpc:call(Node1, mnesia,
  162: 		    move_table_copy, [Tab, Node2, Node1])),
  163:     ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab])),
  164:     ?match({atomic, [{Tab, a, b}]},
  165: 	   rpc:call(Node1, mnesia, transaction, [Read])),
  166:     ?verify_mnesia([Node1], []).
  167: 
  168: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  169: silly_upgrade(doc) ->
  170:     ["Simple test of a schema upgrade and restore from backup"];
  171: silly_upgrade(suite) -> [];
  172: silly_upgrade(Config) when is_list(Config) ->
  173:     [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
  174:     Name = silly_upgrade,
  175:     Tab1 = silly_upgrade1,
  176:     Tab2 = silly_upgrade2,
  177:     Bup = "silly_upgrade.BUP",
  178:     Bup2 = "silly_upgrade_part.BUP",
  179:     ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
  180:     ?match({atomic, ok}, mnesia:create_table(Tab2, [{disc_only_copies, Nodes}])),
  181: 
  182:     CpState = add_some_records(Tab1, Tab2, []),
  183:     ?match(match, verify_state(Tab1, Tab2, CpState)),
  184:     file:delete(Bup),
  185:     ?match(ok, mnesia:backup(Bup)),
  186:     Args = [{name, Name}, {ram_overrides_dump, true},
  187: 	    {min, [Tab1, schema]}, {max, [Tab2]}],
  188:     ?match({ok, Name, _}, mnesia:activate_checkpoint(Args)),
  189: 
  190:     IgnoreState = add_more_records(Tab1, Tab2, CpState),
  191:     ?match(match, verify_state(Tab1, Tab2, IgnoreState)),
  192:     ?match({mismatch, _, _}, verify_state(Tab1, Tab2, CpState)),
  193:     ?match({atomic, ok}, mnesia:del_table_copy(Tab2, Node1)),
  194:     file:delete(Bup2),
  195:     ?match(ok, mnesia:backup_checkpoint(Name, Bup2)),
  196: 
  197:     UpgradeState = transform_some_records(Tab1, Tab2, IgnoreState),
  198:     ?match({mismatch, _, _}, verify_state(Tab1, Tab2, CpState)),
  199:     ?match({mismatch, _, _}, verify_state(Tab1, Tab2, IgnoreState)),
  200:     ?match(match, verify_state(Tab1, Tab2, UpgradeState)),
  201: 
  202:     ?match(ok, mnesia:deactivate_checkpoint(Name)),
  203:     ?match(match, verify_state(Tab1, Tab2, UpgradeState)),
  204: 
  205:     ?match(ok, mnesia:install_fallback(Bup2)),
  206:     file:delete(Bup2),
  207:     %% Will generate intentional crash, fatal error
  208:     ?match([], mnesia_test_lib:stop_mnesia([Node2])),
  209:     wait_till_dead([Node1, Node2]),
  210:     ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab1, Tab2])),
  211:     ?match(match, verify_state(Tab1, Tab2, CpState)),
  212: 
  213:     ?match(ok, mnesia:install_fallback(Bup)),
  214:     file:delete(Bup),
  215:     %% Will generate intentional crash, fatal error
  216:     ?match([], mnesia_test_lib:stop_mnesia([Node1, Node2])),
  217:     wait_till_dead([Node1, Node2]),
  218:     ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab1, Tab2])),
  219:     CpState2 = [X || X <- CpState, element(1, X) /= Tab1],
  220:     ?match(match, verify_state(Tab1, Tab2, CpState2)),
  221:     ?verify_mnesia(Nodes, []).
  222: 
  223: wait_till_dead([]) ->
  224:     ok; %% timer:sleep(5);
  225: wait_till_dead(Repeat = [N|Ns]) ->
  226:     Apps = rpc:call(N, application, which_applications, []),
  227:     case lists:keymember(mnesia, 1, Apps) of
  228: 	true ->
  229: 	    timer:sleep(10),
  230: 	    wait_till_dead(Repeat);
  231: 	false ->
  232: 	    case rpc:call(N, erlang, whereis, [mnesia_monitor]) of
  233: 		undefined ->
  234: 		    wait_till_dead(Ns);
  235: 		_ ->
  236: 		    timer:sleep(10),
  237: 		    wait_till_dead(Repeat)
  238: 	    end
  239:     end.
  240: 
  241: add_some_records(Tab1, Tab2, Old) ->
  242:     Recs1 = [{Tab1, I, I} || I <- lists:seq(1, 30)],
  243:     Recs2 = [{Tab2, I, I} || I <- lists:seq(20, 40)],
  244:     lists:foreach(fun(R) -> mnesia:dirty_write(R) end, Recs1),
  245:     Fun = fun(R) -> mnesia:write(R) end,
  246:     Trans = fun() -> lists:foreach(Fun, Recs2) end,
  247:     ?match({atomic, _}, mnesia:transaction(Trans)),
  248:     lists:sort(Old ++ Recs1 ++ Recs2).
  249: 
  250: add_more_records(Tab1, Tab2, Old) ->
  251:     Change1 = [{T, K, V+100} || {T, K, V} <- Old, K==23],
  252:     Change2 = [{T, K, V+100} || {T, K, V} <- Old, K==24],
  253:     Del = [{T, K} || {T, K, _V} <- Old, K>=25],
  254:     New = [{Tab1, 50, 50}, {Tab2, 50, 50}],
  255:     lists:foreach(fun(R) -> mnesia:dirty_write(R) end, Change1),
  256:     lists:foreach(fun(R) -> mnesia:dirty_delete(R) end, Del),
  257:     Fun = fun(R) -> mnesia:write(R) end,
  258:     Trans = fun() -> lists:foreach(Fun, Change2 ++ New) end,
  259:     ?match({atomic, ok}, mnesia:transaction(Trans)),
  260:     Recs = [{T, K, V} || {T, K, V} <- Old, K<23] ++ Change1 ++ Change2 ++ New,
  261:     lists:sort(Recs).
  262: 
  263: 
  264: verify_state(Tab1, Tab2, Exp) ->
  265:     Fun = fun() ->
  266: 		  Act1 = [mnesia:read({Tab1, K}) || K <- mnesia:all_keys(Tab1)],
  267: 		  Act2 = [mnesia:read({Tab2, K}) || K <- mnesia:all_keys(Tab2)],
  268: 		  Act = lists:append(Act1) ++ lists:append(Act2),
  269: 		  {ok, Act -- Exp, Exp -- Act}
  270: 	  end,
  271:     case mnesia:transaction(Fun) of
  272: 	{atomic, {ok, [], []}} -> match;
  273: 	{atomic, {ok, More, Less}} -> {mismatch, More, Less};
  274: 	{aborted, Reason} -> {error, Reason}
  275:     end.
  276: 
  277: transform_some_records(Tab1, _Tab2, Old) ->
  278:      Fun = fun(Rec) ->
  279: 		   list_to_tuple(tuple_to_list(Rec) ++ [4711])
  280: 	   end,
  281:     ?match({atomic, ok},
  282: 	   mnesia:transform_table(Tab1, Fun, [key, val, extra])),
  283:     Filter = fun(Rec) when element(1, Rec) == Tab1 -> {true, Fun(Rec)};
  284: 		(_) -> true
  285: 	     end,
  286:     lists:sort(lists:zf(Filter, Old)).
  287: 
  288: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  289: 
  290: stress_cases() -> 
  291: [conflict, dist].
  292: 
  293: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  294: dist(doc) ->
  295:     ["Avoid lock conflicts in order to maximize thruput",
  296:      "Ten drivers per node, tables replicated to all nodes, lots of branches"];
  297: dist(suite) -> [];
  298: dist(Config) when is_list(Config) ->
  299:     Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, 10 * 60000}]),
  300:     Storage = mnesia_test_lib:storage_type(disc_copies, Config),
  301:     ?match({ok, _}, mnesia_tpcb:start(dist_args(Nodes, Storage))).
  302: 
  303: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  304: conflict(doc) ->
  305:     ["Provoke a lot of lock conflicts.",
  306:      "Ten drivers per node, tables replicated to all nodes, single branch"];
  307: conflict(suite) -> [];
  308: conflict(Config) when is_list(Config) ->
  309:     Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, 10 * 60000}]),
  310:     Storage = mnesia_test_lib:storage_type(disc_copies, Config),
  311:     ?match({ok, _}, mnesia_tpcb:start(conflict_args(Nodes, Storage))).
  312: 
  313: conflict_args(Nodes, ReplicaType) ->
  314:     [{db_nodes, Nodes},
  315:      {driver_nodes, Nodes},
  316:      {replica_nodes, Nodes},
  317:      {n_drivers_per_node, 10},
  318:      {n_branches, 1},
  319:      {n_accounts_per_branch, 10},
  320:      {replica_type, ReplicaType},
  321:      {stop_after, timer:minutes(5)},
  322:      {report_interval, timer:seconds(10)},
  323:      {use_running_mnesia, true},
  324:      {reuse_history_id, true}].
  325: 
  326: dist_args(Nodes, ReplicaType) ->
  327:     [{db_nodes, Nodes},
  328:      {driver_nodes, Nodes},
  329:      {replica_nodes, Nodes},
  330:      {n_drivers_per_node, 10},
  331:      {n_branches, length(Nodes) * 100},
  332:      {n_accounts_per_branch, 10},
  333:      {replica_type, ReplicaType},
  334:      {stop_after, timer:minutes(5)},
  335:      {report_interval, timer:seconds(10)},
  336:      {use_running_mnesia, true},
  337:      {reuse_history_id, true}].
  338: