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