Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 38 additions & 19 deletions lib/mnesia/src/mnesia_controller.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,33 @@ orphan_tables([Tab | Tabs], Node, Ns, Local, Remote) ->
false when Active == [], DiscCopyHolders == [], RamCopyHoldersOnDiscNodes == [] ->
%% Special case when all replicas resides on disc less nodes
orphan_tables(Tabs, Node, Ns, [Tab | Local], Remote);
false when Active == [], DiscCopyHolders == [],
RamCopyHolders =/= [], not (LocalContent == true) ->
case RamCopyHolders -- Ns of
[] ->
%% We're last up and the other nodes have not
%% loaded the table. Lets load it if we are
%% the smallest node.
case lists:min(RamCopyHolders) of
Min when Min == node() ->
%% This is safe for ram copies because if all nodes are
%% waiting that means the table is empty on all nodes.
%% here we just need to elect a bootstrap node to break
%% the waiting loop.
case mnesia_recover:get_master_nodes(Tab) of
[] ->
L = [Tab | Local],
orphan_tables(Tabs, Node, Ns, L, Remote);
Masters ->
R = [{Tab, Masters} | Remote],
orphan_tables(Tabs, Node, Ns, Local, R)
end;
_ ->
orphan_tables(Tabs, Node, Ns, Local, Remote)
end;
_ ->
orphan_tables(Tabs, Node, Ns, Local, Remote)
end;
_ when LocalContent == true ->
orphan_tables(Tabs, Node, Ns, [Tab | Local], Remote);
_ ->
Expand Down Expand Up @@ -1595,21 +1622,11 @@ update_whereabouts(Tab, Node, State) ->
end.

initial_safe_loads() ->
case val({schema, storage_type}) of
ram_copies ->
Downs = [],
Tabs = val({schema, local_tables}) -- [schema],
LastC = fun(T) -> last_consistent_replica(T, Downs) end,
lists:zf(LastC, Tabs);

disc_copies ->
Downs = mnesia_recover:get_mnesia_downs(),
dbg_out("mnesia_downs = ~p~n", [Downs]),

Tabs = val({schema, local_tables}) -- [schema],
LastC = fun(T) -> last_consistent_replica(T, Downs) end,
lists:zf(LastC, Tabs)
end.
Downs = mnesia_recover:get_mnesia_downs(),
dbg_out("mnesia_downs = ~p~n", [Downs]),
Tabs = val({schema, local_tables}) -- [schema],
LastC = fun(T) -> last_consistent_replica(T, Downs) end,
lists:zf(LastC, Tabs).

last_consistent_replica(Tab, Downs) ->
case ?catch_val({Tab, cstruct}) of
Expand Down Expand Up @@ -1656,12 +1673,14 @@ last_consistent_replica(Cs, Tab, Downs) ->
%% Wait for remote master copy
false;
Storage == ram_copies ->
if
Disc == [], DiscOnly == [], Ext == [] ->
%% Nobody has copy on disc
if
Disc == [], DiscOnly == [], Ext == [], BetterCopies0 == [] ->
%% RAM-only table and no non-down remote copy holder.
%% Safe to load locally.
{true, {Tab, ram_only}};
true ->
%% Some other node has copy on disc
%% Either a disc-resident copy exists or a non-down
%% remote copy holder may have a better copy.
false
end;
AccessMode == read_only ->
Expand Down
47 changes: 30 additions & 17 deletions lib/mnesia/test/mnesia_durability_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,10 @@ load_local_contents_directly(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

load_directly_when_all_are_ram_copiesA(doc) ->
["Tables that are RAM copies only shall also be loaded directly. ",
["Tables that are RAM copies only shall NOT be loaded directly. ",
"1. N1 and N2 has RAM copies of a table, stop N1 before N2. ",
"2. When N1 starts he shall have access to the table ",
" without having to start N2" ];
"2. When N1 starts he shall NOT have access to the table ",
" without loading the better copy from N2" ];
load_directly_when_all_are_ram_copiesA(suite) -> [];
load_directly_when_all_are_ram_copiesA(Config) when is_list(Config) ->
[N1, N2] = Nodes = ?acquire_nodes(2, Config),
Expand All @@ -251,20 +251,21 @@ load_directly_when_all_are_ram_copiesA(Config) when is_list(Config) ->
rpc:call(N2,mnesia,transaction,[Read_one]) ),
%%Stop Mnesia on N2
?match([], mnesia_test_lib:kill_mnesia([N2])),
%%Restart Mnesia on N1 verify that we can access test_rec from
%%N1 without starting Mnesia on N2.
%%Restart Mnesia on N1 verify that we CANNOT access test_rec from
%%N1 without starting Mnesia on N2 because N2 is not in down_nodes of N1 node.
?match(ok, rpc:call(N1, mnesia, start, [])),
?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 30000])),
?match({atomic,[]}, rpc:call(N1,mnesia,transaction,[Read_one])),
?match({atomic,ok}, rpc:call(N1,mnesia,transaction,[Write_one,[33]])),
?match({atomic,[#test_rec{key=2,val=33}]},
?match({timeout, [test_rec]}, rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 30000])),
?match({aborted, {no_exists, test_rec}}, rpc:call(N1,mnesia,transaction,[Read_one])),
?match({aborted, {no_exists, test_rec}}, rpc:call(N1,mnesia,transaction,[Write_one,[33]])),
?match({aborted,{no_exists,test_rec}},
rpc:call(N1,mnesia,transaction,[Read_one])),
%%Restart Mnesia on N2 and verify the contents there.
?match([], mnesia_test_lib:start_mnesia([N2], [test_rec])),
?match( {atomic,[#test_rec{key=2,val=33}]},
?match({atomic, ok}, rpc:call(N1,mnesia,transaction,[Write_one,[44]])),
?match( {atomic,[#test_rec{key=2,val=44}]},
rpc:call(N2, mnesia, transaction, [Read_one] ) ),
%%Check that the start of Mnesai on N2 did not affect the contents on N1
?match( {atomic,[#test_rec{key=2,val=33}]},
?match( {atomic,[#test_rec{key=2,val=44}]},
rpc:call(N1, mnesia, transaction, [Read_one] ) ),
?verify_mnesia(Nodes, []).

Expand Down Expand Up @@ -1350,15 +1351,27 @@ dump_ram_copies(Config) when is_list(Config) ->
%% test_lib:mnesia_start doesn't work, because it waits
%% for the schema on all nodes ... ???
?match(ok,rpc:call(Node3,mnesia,start,[]) ),
?match(ok,rpc:call(Node3,mnesia,wait_for_tables,
[[Tab],timer:seconds(30)] ) ),

%% node3 shall have the contents of the dump
cross_check_tables([C],Tab,{[{Tab,1,4711}],[{Tab,2,42}],[{Tab,3,256}]}),

%% start Mnesia on the other 2 nodes, too
%% node3 is the first-killed node, thus it should wait for better copy
%% from late-killed node to ensure consistency in the cluster
%% to get the better copy, node3 needs to contact the node which has better copy.
?match({timeout, [Tab]},
rpc:call(Node3, mnesia,wait_for_tables,
[[Tab],timer:seconds(3)])),

%% node3 shall NOT serve for this table before it get connected to the better copy.
?match({badrpc,{'EXIT',{aborted,{no_exists,[Tab,1]}}}},
rpc:call(Node3, mnesia, dirty_read, [Tab,1])),

%% start Mnesia on the other 2 nodes now
mnesia_test_lib:start_mnesia([Node1,Node2],[Tab]),


%% node3 should load the better copy and ready to serve.
?match(ok, rpc:call(Node3, mnesia,wait_for_tables,
[[Tab],timer:seconds(30)])),

%% Since all nodes are up
cross_check_tables([A,B,C],Tab,
{[{Tab,1,4711}],[{Tab,2,42}],[{Tab,3,256}]}),
?verify_mnesia(Nodes, []).
Expand Down
Loading