4
%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
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/.
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
21
%%%----------------------------------------------------------------------
22
%%% File : mnesia_evil_backup.erl
23
%%% Author : Dan Gudmundsson <dgud@legolas>
24
%%% Purpose : Evil backup tests
25
%%% Created : 3 Jun 1998 by Dan Gudmundsson <dgud@erix.ericsson.se>
26
%%%----------------------------------------------------------------------
28
-module(mnesia_evil_backup).
29
-author('dgud@erix.ericsson.se').
31
-include("mnesia_test_lib.hrl").
33
%%-export([Function/Arity, ...]).
35
init_per_testcase(Func, Conf) ->
36
mnesia_test_lib:init_per_testcase(Func, Conf).
38
end_per_testcase(Func, Conf) ->
39
mnesia_test_lib:end_per_testcase(Func, Conf).
41
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44
[backup, bad_backup, global_backup_checkpoint,
45
{group, restore_tables}, traverse_backup,
46
selective_backup_checkpoint,
47
incremental_backup_checkpoint, install_fallback,
48
uninstall_fallback, local_fallback,
49
sops_with_checkpoint].
53
[restore_errors, restore_clear, restore_keep,
54
restore_recreate, restore_clear_ram]}].
56
init_per_group(_GroupName, Config) ->
59
end_per_group(_GroupName, Config) ->
63
backup(doc) -> ["Checking the interface to the function backup",
64
"We don't check that the backups can be used here",
65
"That is checked in install_fallback and in restore"];
67
backup(Config) when is_list(Config) ->
68
[Node1, Node2] = _Nodes = ?acquire_nodes(2, Config),
70
Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
71
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
72
?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
73
File = "backup_test.BUP",
74
?match(ok, mnesia:backup(File)),
76
File2 = "backup_test2.BUP",
78
Def2 = [{disc_only_copies, [Node2]}],
79
?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
80
?match(ok, mnesia:backup(File2, mnesia_backup)),
82
File3 = "backup_test3.BUP",
83
mnesia_test_lib:kill_mnesia([Node2]),
84
?match({error, _}, mnesia:backup(File3, mnesia_backup)),
86
?match(ok, file:delete(File)),
87
?match(ok, file:delete(File2)),
88
?match({error, _}, file:delete(File3)),
89
?verify_mnesia([Node1], [Node2]).
92
bad_backup(suite) -> [];
93
bad_backup(Config) when is_list(Config) ->
94
[Node1] = ?acquire_nodes(1, Config),
96
Def = [{disc_copies, [Node1]}],
97
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
98
?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
99
File = "backup_test.BUP",
100
?match(ok, mnesia:backup(File)),
101
file:write_file(File, "trash", [append]),
102
?match(ok, mnesia:dirty_write({Tab, 1, test_bad})),
103
?match({atomic,[Tab]}, mnesia:restore(File, [{clear_tables, [Tab]}])),
104
?match([{Tab,1,test_ok}], mnesia:dirty_read(Tab, 1)),
106
?match(ok, file:delete(File)),
107
?verify_mnesia([Node1], []).
111
global_backup_checkpoint(doc) ->
112
["Checking the interface to the function backup_checkpoint",
113
"We don't check that the backups can be used here",
114
"That is checked in install_fallback and in restore"];
115
global_backup_checkpoint(suite) -> [];
116
global_backup_checkpoint(Config) when is_list(Config) ->
117
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
119
Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
120
File = "backup_checkpoint.BUP",
121
File2 = "backup_checkpoint2.BUP",
122
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
123
?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
124
?match({error, _}, mnesia:backup_checkpoint(cp_name, File)),
125
Spec = [{name, cp_name}, {max, mnesia:system_info(tables)}],
126
?match({ok, _Name, _Ns}, mnesia:activate_checkpoint(Spec)),
127
?match(ok, mnesia:backup_checkpoint(cp_name, File)),
128
?match({error, _}, mnesia:backup_checkpoint(cp_name_nonexist, File)),
129
?match(ok, mnesia:backup_checkpoint(cp_name, File2, mnesia_backup)),
130
?match({error, _}, file:delete(File)),
131
?match(ok, file:delete(File2)),
132
?verify_mnesia(Nodes, []).
135
restore_errors(suite) -> [];
136
restore_errors(Config) when is_list(Config) ->
137
[_Node] = ?acquire_nodes(1, Config),
138
?match({aborted, enoent}, mnesia:restore(notAfile, [])),
139
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, not_a_list)),
140
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [test_badarg])),
141
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{test_badarg, xxx}])),
142
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{skip_tables, xxx}])),
143
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{recreate_tables, [schema]}])),
144
?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{default_op, asdklasd}])),
147
restore_clear(suite) -> [];
148
restore_clear(Config) when is_list(Config) ->
149
restore(Config, clear_tables).
151
restore_keep(suite) -> [];
152
restore_keep(Config) when is_list(Config) ->
153
restore(Config, keep_tables).
155
restore_recreate(suite) -> [];
156
restore_recreate(Config) when is_list(Config) ->
157
restore(Config, recreate_tables).
159
check_tab(Records, Line) ->
160
Verify = fun({Table, Key, Val}) ->
161
case catch mnesia:dirty_read({Table, Key}) of
162
[{Table, Key, Val}] -> ok;
164
mnesia_test_lib:error("Not matching on Node ~p ~n"
165
" Expected ~p~n Actual ~p~n",
166
[node(), {Table, Key, Val}, Else],
171
[{Tab, Key, _}, _] = Recs,
172
SRecs = lists:sort(Recs),
173
R_Recs = lists:sort(catch mnesia:dirty_read({Tab, Key})),
177
mnesia_test_lib:error("Not matching on Node ~p ~n"
178
" Expected ~p~n Actual ~p~n",
179
[node(), SRecs, Else],
184
lists:foreach(Verify, Records).
186
restore(Config, Op) ->
187
[Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
190
Def1 = [{snmp, [{key, integer}]}, {ram_copies, [Node1]}],
192
Def2 = [{index, [val]}, {disc_copies, [Node1, Node2]}],
194
Def3 = [{type, bag}, {disc_only_copies, Nodes}],
195
?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
196
?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
197
?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
199
File1 = "restore1.BUP",
200
File2 = "restore2.BUP",
202
Restore = fun(O, A) ->
203
case mnesia:restore(O, A) of
204
{atomic, Tabs} when is_list(Tabs) -> {atomic, lists:sort(Tabs)};
208
Tabs = lists:sort([Tab1, Tab2, Tab3]),
210
[mnesia:dirty_write({Tab1, N, N+42}) || N <- lists:seq(1, 10)],
211
[mnesia:dirty_write({Tab2, N, N+43}) || N <- lists:seq(1, 10)],
212
[mnesia:dirty_write({Tab3, N, N+44}) || N <- lists:seq(1, 10)],
214
Res1 = [{Tab1, N, N+42} || N <- lists:seq(1, 10)],
215
Res2 = [{Tab2, N, N+43} || N <- lists:seq(1, 10)],
216
Res3 = [{Tab3, N, N+44} || N <- lists:seq(1, 10)],
218
{ok, Name, _} = mnesia:activate_checkpoint([{min, Tabs}, {ram_overrides_dump, true}]),
221
%% Test standard Restore on one table on one node
222
?match(ok, mnesia:backup_checkpoint(Name, File1)),
223
?match(ok, mnesia:deactivate_checkpoint(Name)),
224
?match(ok, mnesia:backup(File2)),
225
[mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
226
[mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
227
[mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
228
_Res11 = [{Tab1, N, N+1} || N <- lists:seq(1, 11)],
229
Res21 = [{Tab2, N, N+1} || N <- lists:seq(1, 11)],
230
Res31 = [[{Tab3, N, N+1}, {Tab3, N, N+44}] || N <- lists:seq(1, 10)],
232
?match({atomic, [Tab1]}, Restore(File1, [{Op, [Tab1]},
233
{skip_tables, Tabs -- [Tab1]}])),
236
?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11}));
238
?match([], mnesia:dirty_read({Tab1, 11}));
240
?match([], mnesia:dirty_read({Tab1, 11}))
242
[rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
243
[rpc:call(Node, ?MODULE, check_tab, [Res21, ?LINE]) || Node <- Nodes],
244
[rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
246
%% Restore all tables on it's nodes
247
mnesia_schema:clear_table(Tab1),
248
mnesia_schema:clear_table(Tab2),
249
mnesia_schema:clear_table(Tab3),
250
[mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
251
[mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
252
[mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
254
?match({atomic, ok}, mnesia:del_table_copy(Tab2, Node1)),
256
?match({ok, Node1}, mnesia:subscribe({table, Tab1})),
258
?match({atomic, Tabs}, Restore(File1, [{default_op, Op},
259
{module, mnesia_backup}])),
262
?match_receive({mnesia_table_event, {delete, {schema, Tab1}, _}}),
263
?match_receive({mnesia_table_event, {write, {schema, Tab1, _}, _}}),
265
[rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
266
[rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
267
[rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
268
?match([], mnesia:dirty_read({Tab1, 11})),
269
?match([], mnesia:dirty_read({Tab2, 11})),
270
?match([], mnesia:dirty_read({Tab3, 11})),
272
?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
273
?match([], mnesia:dirty_index_read(Tab2, 11, val)),
275
?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
276
?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
277
?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
279
?match([Node2], mnesia:table_info(Tab2, where_to_write));
282
[rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
283
[rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
284
[rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
285
?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11})),
286
?match([{Tab2, 11, 12}], mnesia:dirty_read({Tab2, 11})),
287
?match([{Tab3, 11, 12}], mnesia:dirty_read({Tab3, 11})),
288
?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
290
?match([], mnesia:dirty_index_read(Tab2, 11, val)),
291
?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
293
?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
294
?match({ok, {Tab1, 11, 12}}, mnesia:snmp_get_row(Tab1, [11])),
296
?match([Node2], mnesia:table_info(Tab2, where_to_write));
298
check_subscr(Tab1, 0),
299
[rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
300
[rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
301
[rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
302
?match([], mnesia:dirty_read({Tab1, 11})),
303
?match([], mnesia:dirty_read({Tab2, 11})),
304
?match([], mnesia:dirty_read({Tab3, 11})),
306
?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
307
?match([], mnesia:dirty_index_read(Tab2, 11, val)),
309
?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
310
?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
311
?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
313
Ns = lists:sort([Node1, Node2]),
314
?match(Ns, lists:sort(mnesia:table_info(Tab2, where_to_write)))
316
?match(ok, file:delete(File1)),
317
?match(ok, file:delete(File2)),
318
?verify_mnesia(Nodes, []).
322
check_subscr(Tab, 10).
324
check_subscr(_Tab, 0) ->
327
?error("Too many msgs ~p~n", [Msg])
331
check_subscr(Tab, N) ->
334
{mnesia_table_event, {write, {Tab, N, V}, _}} ->
335
check_subscr(Tab, N-1)
337
?error("Missing ~p~n", [{Tab, N, V}])
340
restore_clear_ram(suite) -> [];
341
restore_clear_ram(Config) when is_list(Config) ->
342
Nodes = ?acquire_nodes(3, [{diskless, true}|Config]),
344
?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
347
mnesia:write({a,1,What}),
348
mnesia:write({a,2,What}),
349
mnesia:write({a,3,What})
351
Bup = "restore_clear_ram.BUP",
353
?match({atomic, ok}, mnesia:transaction(Write, [initial])),
354
?match({ok, _, _}, mnesia:activate_checkpoint([{name,test},
356
{ram_overrides_dump, true}])),
357
?match(ok, mnesia:backup_checkpoint(test, Bup)),
359
?match({atomic, ok}, mnesia:transaction(Write, [data])),
360
?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
362
restore_clear_ram_loop(100, Nodes, Bup),
366
restore_clear_ram_loop(N, Nodes = [N1,N2,N3], Bup) when N > 0 ->
367
?match([], mnesia_test_lib:stop_mnesia(Nodes)),
368
?match({_, []}, rpc:multicall([N1,N2], mnesia, start, [[{extra_db_nodes, Nodes}]])),
369
Key = rpc:async_call(N3, mnesia, start, [[{extra_db_nodes, Nodes}]]),
370
?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
371
?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
372
?match(ok, rpc:yield(Key)),
373
?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[a], 3000])),
374
case rpc:multicall(Nodes, mnesia, table_info, [a,size]) of
376
restore_clear_ram_loop(N-1, Nodes, Bup);
380
restore_clear_ram_loop(_,_,_) ->
383
traverse_backup(doc) ->
384
["Testing the traverse_backup interface, the resulting file is not tested though",
385
"See install_fallback for result using the output file from traverse_backup",
386
"A side effect is that the backup file contents are tested"];
387
traverse_backup(suite) -> [];
388
traverse_backup(Config) when is_list(Config) ->
389
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
391
Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
392
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
393
?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
394
?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
395
?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
396
?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
397
?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
398
File = "_treverse_backup.BUP",
399
File2 = "traverse_backup2.BUP",
400
File3 = "traverse_backup3.BUP",
401
?match(ok, mnesia:backup(File)),
403
Fun = fun({backup_tab, N, _}, Acc) -> {[{backup_tab, N, test_ok}], Acc+1};
404
(Other, Acc) -> {[Other], Acc}
407
?match({ok, 5}, mnesia:traverse_backup(File, read_only, Fun, 0)),
408
?match(ok, file:delete(read_only)),
410
?match({ok, 5}, mnesia:traverse_backup(File, mnesia_backup,
411
dummy, read_only, Fun, 0)),
413
?match({ok, 5}, mnesia:traverse_backup(File, File2, Fun, 0)),
414
?match({ok, 5}, mnesia:traverse_backup(File2, mnesia_backup,
415
File3, mnesia_backup, Fun, 0)),
417
BadFun = fun({bad_tab, _N, asd}, Acc) -> {{error, error}, Acc} end,
418
?match({error, _}, mnesia:traverse_backup(File, read_only, BadFun, 0)),
419
?match({error, _}, file:delete(read_only)),
420
?match(ok, file:delete(File)),
421
?match(ok, file:delete(File2)),
422
?match(ok, file:delete(File3)),
423
?verify_mnesia(Nodes, []).
426
install_fallback(doc) ->
427
["This tests the install_fallback intf.",
428
"It also verifies that the output from backup_checkpoint and traverse_backup",
430
install_fallback(suite) -> [];
431
install_fallback(Config) when is_list(Config) ->
432
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
433
Tab = fallbacks_test,
434
Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
435
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
436
?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
437
?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
438
?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
439
?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
440
?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
442
Tab2 = fallbacks_test2,
443
Def2 = [{disc_copies, [node()]}],
444
?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
445
Tab3 = fallbacks_test3,
446
?match({atomic, ok}, mnesia:create_table(Tab3, Def2)),
448
Rec = {Tab2, Key, test_ok},
449
mnesia:dirty_write(Rec),
453
OldRecs2 = [Fun2(K) || K <- lists:seq(1, TabSize3)],
455
Spec =[{name, cp_name}, {max, mnesia:system_info(tables)}],
456
?match({ok, _Name, Nodes}, mnesia:activate_checkpoint(Spec)),
457
?match(ok, mnesia:dirty_write({Tab, 6, test_nok})),
458
[mnesia:dirty_write({Tab2, K, test_nok}) || K <- lists:seq(1, TabSize3 + 10)],
459
File = "install_fallback.BUP",
460
File2 = "install_fallback2.BUP",
461
File3 = "install_fallback3.BUP",
462
?match(ok, mnesia:backup_checkpoint(cp_name, File)),
464
Fun = fun({T, N, _}, Acc) when T == Tab ->
467
io:format("write ~p -> ~p~n", [N, T]),
468
{[{T, N, test_ok}], Acc + 1};
470
io:format("write ~p -> ~p~n", [N, Tab3]),
471
{[{Tab3, N, test_ok}], Acc + 1}
473
({T, N}, Acc) when T == Tab ->
476
io:format("delete ~p -> ~p~n", [N, T]),
479
io:format("delete ~p -> ~p~n", [N, Tab3]),
480
{[{Tab3, N}], Acc + 1}
485
?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
486
?match(ok, mnesia:install_fallback(File2)),
488
mnesia_test_lib:kill_mnesia([Node1, Node2]),
489
timer:sleep(timer:seconds(1)), % Let it die!
491
?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab, Tab2, Tab3])),
494
?match([], mnesia:dirty_read({Tab, 1})),
495
?match([{Tab3, 1, test_ok}], mnesia:dirty_read({Tab3, 1})),
496
?match([{Tab, 2, test_ok}], mnesia:dirty_read({Tab, 2})),
497
?match([], mnesia:dirty_read({Tab3, 2})),
498
?match([], mnesia:dirty_read({Tab, 3})),
499
?match([{Tab3, 3, test_ok}], mnesia:dirty_read({Tab3, 3})),
500
?match([{Tab, 4, test_ok}], mnesia:dirty_read({Tab, 4})),
501
?match([], mnesia:dirty_read({Tab3, 4})),
502
?match([], mnesia:dirty_read({Tab, 5})),
503
?match([{Tab3, 5, test_ok}], mnesia:dirty_read({Tab3, 5})),
504
?match([], mnesia:dirty_read({Tab, 6})),
505
?match([], mnesia:dirty_read({Tab3, 6})),
506
?match([], [mnesia:dirty_read({Tab2, K}) || K <- lists:seq(1, TabSize3)] -- OldRecs2),
507
?match(TabSize3, mnesia:table_info(Tab2, size)),
509
% Check the interface
511
?match({error, _}, mnesia:install_fallback(File3)),
512
?match({error, _}, mnesia:install_fallback(File2, mnesia_badmod)),
513
?match(ok, mnesia:install_fallback(File2, mnesia_backup)),
514
?match(ok, file:delete(File)),
515
?match(ok, file:delete(File2)),
516
?match({error, _}, file:delete(File3)),
517
?verify_mnesia(Nodes, []).
519
uninstall_fallback(suite) -> [];
520
uninstall_fallback(Config) when is_list(Config) ->
521
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
522
Tab = uinst_fallbacks_test,
523
File = "uinst_fallback.BUP",
524
File2 = "uinst_fallback2.BUP",
525
Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
526
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
527
?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
528
?match(ok, mnesia:backup(File)),
529
Fun = fun({T, N, _}, Acc) when T == Tab ->
530
{[{T, N, test_nok}], Acc+1};
531
(Other, Acc) -> {[Other], Acc}
533
?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
534
?match({error, enoent}, mnesia:uninstall_fallback()),
535
?match(ok, mnesia:install_fallback(File2)),
536
?match(ok, file:delete(File)),
537
?match(ok, file:delete(File2)),
538
?match(ok, mnesia:uninstall_fallback()),
540
mnesia_test_lib:kill_mnesia([Node1, Node2]),
541
timer:sleep(timer:seconds(1)), % Let it die!
542
?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab])),
543
?match([{Tab, 1, test_ok}], mnesia:dirty_read({Tab, 1})),
544
?verify_mnesia(Nodes, []).
546
local_fallback(suite) -> [];
547
local_fallback(Config) when is_list(Config) ->
548
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
549
Tab = local_fallback,
550
File = "local_fallback.BUP",
551
Def = [{disc_copies, Nodes}],
553
Pre = {Tab, Key, pre},
554
Post = {Tab, Key, post},
555
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
556
?match(ok, mnesia:dirty_write(Pre)),
557
?match(ok, mnesia:backup(File)),
558
?match(ok, mnesia:dirty_write(Post)),
559
Local = [{scope, local}],
560
?match({error, enoent}, mnesia:uninstall_fallback(Local)),
561
?match(ok, mnesia:install_fallback(File, Local)),
562
?match(true, mnesia:system_info(fallback_activated)),
563
?match(ok, mnesia:uninstall_fallback(Local)),
564
?match(false, mnesia:system_info(fallback_activated)),
565
?match(ok, mnesia:install_fallback(File, Local)),
566
?match(true, mnesia:system_info(fallback_activated)),
568
?match(false, rpc:call(Node2, mnesia, system_info , [fallback_activated])),
569
?match(ok, rpc:call(Node2, mnesia, install_fallback , [File, Local])),
570
?match([Post], mnesia:dirty_read({Tab, Key})),
571
?match([Post], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
573
?match([], mnesia_test_lib:kill_mnesia(Nodes)),
574
?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
575
?match([Pre], mnesia:dirty_read({Tab, Key})),
576
?match([Pre], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
577
Dir = rpc:call(Node2, mnesia, system_info , [directory]),
579
?match(ok, mnesia:dirty_write(Post)),
580
?match([Post], mnesia:dirty_read({Tab, Key})),
581
?match([], mnesia_test_lib:kill_mnesia([Node2])),
582
?match(ok, mnesia:install_fallback(File, Local ++ [{mnesia_dir, Dir}])),
583
?match([], mnesia_test_lib:kill_mnesia([Node1])),
585
?match([], mnesia_test_lib:start_mnesia([Node2], [])),
586
?match(yes, rpc:call(Node2, mnesia, force_load_table, [Tab])),
587
?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
588
?match([Pre], mnesia:dirty_read({Tab, Key})),
590
?match(ok, file:delete(File)),
591
?verify_mnesia(Nodes, []).
593
selective_backup_checkpoint(doc) ->
594
["Perform a selective backup of a checkpoint"];
595
selective_backup_checkpoint(suite) -> [];
596
selective_backup_checkpoint(Config) when is_list(Config) ->
597
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
599
OmitTab = sel_backup_omit,
601
Def = [{disc_copies, [Node1, Node2]}],
602
File = "selective_backup_checkpoint.BUP",
603
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
604
?match({atomic, ok}, mnesia:create_table(OmitTab, Def)),
605
?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
606
?match(ok, mnesia:dirty_write({OmitTab, 1, test_ok})),
607
CpSpec = [{name, CpName}, {max, mnesia:system_info(tables)}],
608
?match({ok, CpName, _Ns}, mnesia:activate_checkpoint(CpSpec)),
610
BupSpec = [{tables, [Tab]}],
611
?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec)),
613
?match([schema, sel_backup], bup_tables(File, mnesia_backup)),
614
?match(ok, file:delete(File)),
616
BupSpec2 = [{tables, [Tab, OmitTab]}],
617
?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec2)),
619
?match([schema, sel_backup, sel_backup_omit],
620
bup_tables(File, mnesia_backup)),
621
?match(ok, file:delete(File)),
622
?verify_mnesia(Nodes, []).
624
bup_tables(File, Mod) ->
625
Fun = fun(Rec, Tabs) ->
626
Tab = element(1, Rec),
627
Tabs2 = [Tab | lists:delete(Tab, Tabs)],
630
case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
637
incremental_backup_checkpoint(doc) ->
638
["Perform a incremental backup of a checkpoint"];
639
incremental_backup_checkpoint(suite) -> [];
640
incremental_backup_checkpoint(Config) when is_list(Config) ->
641
[Node1] = Nodes = ?acquire_nodes(1, Config),
643
Def = [{disc_copies, [Node1]}],
644
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
645
OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
646
?match([ok|_], [mnesia:dirty_write(R) || R <- OldRecs]),
648
OldCpSpec = [{name, OldCpName}, {min, [Tab]}],
649
?match({ok, OldCpName, _Ns}, mnesia:activate_checkpoint(OldCpSpec)),
651
BupSpec = [{tables, [Tab]}],
652
OldFile = "old_full_backup.BUP",
653
?match(ok, mnesia:backup_checkpoint(OldCpName, OldFile, BupSpec)),
654
?match(OldRecs, bup_records(OldFile, mnesia_backup)),
655
?match(ok, mnesia:dirty_delete({Tab, 1})),
656
?match(ok, mnesia:dirty_write({Tab, 2, 2})),
657
?match(ok, mnesia:dirty_write({Tab, 3, -3})),
660
NewCpSpec = [{name, NewCpName}, {min, [Tab]}],
661
?match({ok, NewCpName, _Ns}, mnesia:activate_checkpoint(NewCpSpec)),
662
?match(ok, mnesia:dirty_write({Tab, 4, 4})),
664
NewFile = "new_full_backup.BUP",
665
?match(ok, mnesia:backup_checkpoint(NewCpName, NewFile, BupSpec)),
666
NewRecs = [{Tab, 2, 2}, {Tab, 3, -3},
667
{Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}, {Tab, 5, -5}],
668
?match(NewRecs, bup_records(NewFile, mnesia_backup)),
670
DiffFile = "diff_backup.BUP",
671
DiffBupSpec = [{tables, [Tab]}, {incremental, OldCpName}],
672
?match(ok, mnesia:backup_checkpoint(NewCpName, DiffFile, DiffBupSpec)),
673
DiffRecs = [{Tab, 1}, {Tab, 2}, {Tab, 2, 2}, {Tab, 3}, {Tab, 3, -3},
674
{Tab, 4}, {Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}],
675
?match(DiffRecs, bup_records(DiffFile, mnesia_backup)),
677
?match(ok, mnesia:deactivate_checkpoint(OldCpName)),
678
?match(ok, mnesia:deactivate_checkpoint(NewCpName)),
679
?match(ok, file:delete(OldFile)),
680
?match(ok, file:delete(NewFile)),
681
?match(ok, file:delete(DiffFile)),
683
?verify_mnesia(Nodes, []).
685
bup_records(File, Mod) ->
686
Fun = fun(Rec, Recs) when element(1, Rec) == schema ->
689
{[Rec], [Rec | Recs]}
691
case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
693
lists:keysort(1, lists:keysort(2, lists:reverse(Recs)));
698
sops_with_checkpoint(doc) ->
699
["Test schema operations during a checkpoint"];
700
sops_with_checkpoint(suite) -> [];
701
sops_with_checkpoint(Config) when is_list(Config) ->
702
Ns = ?acquire_nodes(2, Config),
704
?match({ok, cp1, Ns}, mnesia:activate_checkpoint([{name, cp1},{max,mnesia:system_info(tables)}])),
706
?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies,Ns}])),
707
OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
708
[mnesia:dirty_write(R) || R <- OldRecs],
710
?match({ok, cp2, Ns}, mnesia:activate_checkpoint([{name, cp2},{max,mnesia:system_info(tables)}])),
711
File1 = "cp1_delete_me.BUP",
712
?match(ok, mnesia:dirty_write({Tab,6,-6})),
713
?match(ok, mnesia:backup_checkpoint(cp1, File1)),
714
?match(ok, mnesia:dirty_write({Tab,7,-7})),
715
File2 = "cp2_delete_me.BUP",
716
?match(ok, mnesia:backup_checkpoint(cp2, File2)),
718
?match(ok, mnesia:deactivate_checkpoint(cp1)),
719
?match(ok, mnesia:backup_checkpoint(cp2, File1)),
720
?match(ok, mnesia:dirty_write({Tab,8,-8})),
722
?match({atomic,ok}, mnesia:delete_table(Tab)),
723
?match({error,_}, mnesia:backup_checkpoint(cp2, File2)),
724
?match({'EXIT',_}, mnesia:dirty_write({Tab,9,-9})),
726
?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])),
727
Test = fun(N) when N > 5 -> ?error("To many records in backup ~p ~n", [N]);
728
(N) -> case mnesia:dirty_read(Tab,N) of
729
[{Tab,N,B}] when -B =:= N -> ok;
730
Other -> ?error("Not matching ~p ~p~n", [N,Other])
733
[Test(N) || N <- mnesia:dirty_all_keys(Tab)],
734
?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])),
736
file:delete(File1), file:delete(File2),
738
?verify_mnesia(Ns, []).