Skip to content

Commit 6b7b829

Browse files
committed
Fix mnesia:force_load_table/1 getting stuck
1 parent b6b08e1 commit 6b7b829

3 files changed

Lines changed: 157 additions & 46 deletions

File tree

lib/mnesia/src/mnesia_controller.erl

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,8 @@ handle_info(#dumper_done{worker_pid=Pid, worker_res=Res}, State) ->
11861186

11871187
handle_info(Done = #loader_done{worker_pid=WPid, table_name=Tab}, State0) ->
11881188
LateQueue0 = State0#state.late_loader_queue,
1189-
State1 = State0#state{loader_pid = lists:keydelete(WPid,1,get_loaders(State0))},
1189+
{value, {WPid, Worker}, Loaders} = lists:keytake(WPid, 1, get_loaders(State0)),
1190+
State1 = State0#state{loader_pid = Loaders},
11901191

11911192
State2 =
11921193
case Done#loader_done.is_loaded of
@@ -1235,19 +1236,25 @@ handle_info(Done = #loader_done{worker_pid=WPid, table_name=Tab}, State0) ->
12351236
ignore
12361237
end,
12371238

1238-
case {?catch_val({Tab, storage_type}), val({Tab, active_replicas})} of
1239-
{unknown, _} -> %% Should not have a local copy anymore
1240-
State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)};
1241-
{_, [_|_]} -> % still available elsewhere
1242-
{value,{_,Worker}} = lists:keysearch(WPid,1,get_loaders(State0)),
1243-
add_loader(Tab,Worker,State1);
1244-
{ram_copies, []} ->
1245-
DelState = State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)},
1246-
cast({disc_load, Tab, ram_only}),
1247-
DelState;
1248-
{_, []} -> %% Table deleted or not loaded anywhere
1249-
State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)}
1250-
end
1239+
case {?catch_val({Tab, storage_type}), val({Tab, active_replicas}), val({Tab, load_by_force})} of
1240+
{unknown, _, _} -> %% Should not have a local copy anymore
1241+
State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)};
1242+
{_, [_|_], _} -> % still available elsewhere
1243+
add_loader(Tab,Worker,State1);
1244+
{ram_copies, [], _} ->
1245+
DelState = State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)},
1246+
cast({disc_load, Tab, ram_only}),
1247+
DelState;
1248+
{_, [], true} when is_record(Worker, net_load) ->
1249+
%% Network load could have been aborted by Sender node going down,
1250+
%% if user has forced the load, we retry loading from disc
1251+
DelState = State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)},
1252+
cast({disc_load, Tab, forced_by_user}),
1253+
DelState;
1254+
{_, [], _} ->
1255+
%% Table deleted or not loaded anywhere
1256+
State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)}
1257+
end
12511258
end,
12521259
State3 = opt_start_worker(State2),
12531260
noreply(State3);

lib/mnesia/src/mnesia_loader.erl

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
net_load_table/4,
3434
send_table/4]).
3535

36-
-export([spawned_receiver/8]). %% Spawned lock taking process
36+
-export([spawned_receiver/7]). %% Spawned lock taking process
3737

3838
-import(mnesia_lib, [set/2, fatal/2, verbose/2, dbg_out/2]).
3939

@@ -271,24 +271,23 @@ init_receiver(Node, Tab,Storage,Cs,Reason) ->
271271
mnesia_locker:rlock(Tid, Ts#tidstore.store, {schema, Tab});
272272
_ -> ok
273273
end,
274-
%% Check that table still exists
275-
Active = val({Tab, active_replicas}),
276-
%% Check that we haven't loaded it already
277-
case val({Tab,where_to_read}) == node() of
278-
true -> ok;
279-
_ ->
280-
%% And that sender still got a copy
281-
true = lists:member(Node, Active),
282-
{SenderPid, TabSize, DetsData} =
283-
start_remote_sender(Node,Tab,Storage,load),
284-
Init = table_init_fun(SenderPid, Storage),
285-
Args = [self(),Tab,Storage,Cs,SenderPid,
286-
TabSize,DetsData,Init],
287-
Pid = spawn_link(?MODULE, spawned_receiver, Args),
288-
put(mnesia_real_loader, Pid),
289-
wait_on_load_complete(Pid)
290-
end
291-
end,
274+
%% Check that table still exists
275+
Active = val({Tab, active_replicas}),
276+
%% Check that we haven't loaded it already
277+
case val({Tab,where_to_read}) == node() of
278+
true -> ok;
279+
_ ->
280+
%% And that sender still got a copy
281+
true = lists:member(Node, Active),
282+
{SenderPid, TabSize, DetsData} =
283+
start_remote_sender(Node,Tab,Storage,load),
284+
Args = [self(),Tab,Storage,Cs,SenderPid,
285+
TabSize,DetsData],
286+
Pid = spawn_link(?MODULE, spawned_receiver, Args),
287+
put(mnesia_real_loader, Pid),
288+
wait_on_load_complete(Pid)
289+
end
290+
end,
292291
Res =
293292
case mnesia:transaction(Load, 20) of
294293
{atomic, {error,Result}} when
@@ -339,17 +338,36 @@ start_remote_sender(Node,Tab,Storage, Why) ->
339338
end.
340339

341340
table_init_fun(SenderPid, Storage) ->
341+
Parent = self(),
342342
fun(read) ->
343-
% We want to store subscribed mnesia table events received during
344-
% table copying for later processing to not let receiver message queue
345-
% to grow too much (which in consequence would slow down the whole copying process)
346-
SubscrCache = ets:new(subscr_cache, [private, ordered_set]),
347-
put(mnesia_table_receiver_subscr_cache, SubscrCache),
348-
Receiver = self(),
349-
SenderPid ! {Receiver, more},
350-
get_data(SenderPid, Receiver, Storage);
343+
% If this function is executed by a pid different then the loader pid, we have to
344+
% register with controller to receive {copier_done, Node}. This is happening
345+
% with disc_only_copies, where this is executed by dets_server.
346+
case self() of
347+
Parent ->
348+
ok;
349+
_ ->
350+
ok = mnesia_controller:call({add_other, self()})
351+
end,
352+
try
353+
% We want to store subscribed mnesia table events received during
354+
% table copying for later processing to not let receiver message queue
355+
% to grow too much (which in consequence would slow down the whole copying process)
356+
SubscrCache = ets:new(subscr_cache, [private, ordered_set]),
357+
put(mnesia_table_receiver_subscr_cache, SubscrCache),
358+
Receiver = self(),
359+
SenderPid ! {Receiver, more},
360+
get_data(SenderPid, Receiver, Storage)
361+
after
362+
case self() of
363+
Parent ->
364+
ok;
365+
_ ->
366+
mnesia_controller:call({del_other, self()})
367+
end
368+
end;
351369
(close) ->
352-
ok
370+
ok
353371
end.
354372

355373
%% Add_table_copy gets it's own locks.
@@ -364,8 +382,9 @@ start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,{add_table_copy
364382
Else
365383
end.
366384

367-
spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData, Init) ->
385+
spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData) ->
368386
process_flag(trap_exit, true),
387+
Init = table_init_fun(SenderPid, Storage),
369388
Done = do_init_table(Tab,Storage,Cs,
370389
SenderPid,TabSize,DetsData,
371390
ReplyTo, Init),

lib/mnesia/test/mnesia_durability_test.erl

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
master_on_non_local_tables/1,
5050
remote_force_load_with_local_master_node/1,
5151
master_node_with_ram_copy_2/1, master_node_with_ram_copy_3/1,
52+
force_load_table_when_loader_aborts_ram/1,
53+
force_load_table_when_loader_aborts_disc/1,
54+
force_load_table_when_loader_aborts_disc_only/1,
55+
force_load_table_when_loader_aborts_ext_ram/1,
56+
force_load_table_when_loader_aborts_ext_disc_only/1,
57+
force_load_table_when_loader_on_different_pid/1,
5258
dump_ram_copies/1, dump_disc_copies/1, dump_disc_only/1]).
5359

5460
-include("mnesia_test_lib.hrl").
@@ -71,7 +77,7 @@ all() ->
7177
durability_of_disc_copies,
7278
durability_of_disc_only_copies].
7379

74-
groups() ->
80+
groups() ->
7581
[{load_tables, [],
7682
[load_latest_data, load_local_contents_directly,
7783
load_directly_when_all_are_ram_copiesA,
@@ -97,8 +103,18 @@ groups() ->
97103
remote_force_load_with_local_master_node,
98104
master_node_with_ram_copy_2, master_node_with_ram_copy_3]},
99105
{durability_of_dump_tables, [],
100-
[dump_ram_copies, dump_disc_copies, dump_disc_only]}].
101-
106+
[dump_ram_copies, dump_disc_copies, dump_disc_only]},
107+
{load_tables_with_network_down, [],
108+
[force_load_table_when_loader_aborts_ram,
109+
force_load_table_when_loader_aborts_disc,
110+
force_load_table_when_loader_aborts_disc_only,
111+
force_load_table_when_loader_aborts_ext_ram,
112+
force_load_table_when_loader_aborts_ext_disc_only,
113+
force_load_table_when_loader_on_different_pid]
114+
}].
115+
116+
init_per_group(load_tables_with_network_down, Config) ->
117+
mnesia_test_lib:skip_if_no_network_blocker(Config);
102118
init_per_group(_GroupName, Config) ->
103119
Config.
104120

@@ -1270,6 +1286,75 @@ master_node_with_ram_copy_3(Config) when is_list(Config) ->
12701286

12711287
?verify_mnesia(Nodes, []).
12721288

1289+
force_load_table_when_loader_aborts_ram(Config) ->
1290+
force_load_table_when_loader_aborts(Config, ram_copies).
1291+
1292+
force_load_table_when_loader_aborts_disc(Config) ->
1293+
force_load_table_when_loader_aborts(Config, disc_copies).
1294+
1295+
force_load_table_when_loader_aborts_disc_only(Config) ->
1296+
force_load_table_when_loader_aborts(Config, disc_only_copies).
1297+
1298+
force_load_table_when_loader_aborts_ext_ram(Config) ->
1299+
force_load_table_when_loader_aborts(Config, ext_ram_copies).
1300+
1301+
force_load_table_when_loader_aborts_ext_disc_only(Config) ->
1302+
force_load_table_when_loader_aborts(Config, ext_disc_only_copies).
1303+
1304+
force_load_table_when_loader_aborts(Config, Storage) ->
1305+
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
1306+
Table = ?FUNCTION_NAME,
1307+
Populate = fun(F, N) when N > 0 ->
1308+
mnesia:write({Table, N, []}),
1309+
F(F, N - 1);
1310+
(_F, _N) ->
1311+
ok
1312+
end,
1313+
?match({atomic, ok}, mnesia:create_table(Table, [{Storage, Nodes}])),
1314+
?match({atomic, ok}, mnesia:transaction(fun() -> Populate(Populate, 1_000_000) end)),
1315+
?match(ok, mnesia:set_master_nodes([Node2])),
1316+
?match(stopped, mnesia:stop()),
1317+
?match(ok, mnesia:start()),
1318+
?match(ok, mnesia_test_lib:block_peer(Node1, Node2)),
1319+
?match({timeout, [Table]}, mnesia:wait_for_tables([Table], 0)),
1320+
?match(pang, net_adm:ping(Node2)),
1321+
{Pid, Ref} = spawn_monitor(fun() -> yes = mnesia:force_load_table(Table) end),
1322+
receive
1323+
{'DOWN', Ref, process, Pid, _} ->
1324+
ok
1325+
after timer:seconds(15) ->
1326+
ct:fail("Pid: ~p stuck in:~n~p~n",
1327+
[Pid, process_info(Pid, current_stacktrace)])
1328+
end.
1329+
1330+
force_load_table_when_loader_on_different_pid(Config) ->
1331+
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
1332+
Table = ?FUNCTION_NAME,
1333+
Populate = fun(F, N) when N > 0 ->
1334+
mnesia:write({Table, N, []}),
1335+
F(F, N - 1);
1336+
(_F, _N) ->
1337+
ok
1338+
end,
1339+
?match({atomic, ok}, mnesia:create_table(Table, [{disc_only_copies, Nodes}])),
1340+
?match({atomic, ok}, mnesia:transaction(fun() -> Populate(Populate, 500_000) end)),
1341+
?match(ok, mnesia:set_master_nodes([Node2])),
1342+
?match(stopped, mnesia:stop()),
1343+
?match(ok, mnesia:start()),
1344+
?match(ok, mnesia_test_lib:block_peer(Node1, Node2)),
1345+
?match({timeout, [Table]}, mnesia:wait_for_tables([Table], 0)),
1346+
?match(pang, net_adm:ping(Node2)),
1347+
?match(ok, timer:sleep(timer:seconds(1))),
1348+
{Pid, Ref} = spawn_monitor(fun() -> yes = mnesia:force_load_table(Table) end),
1349+
receive
1350+
{'DOWN', Ref, process, Pid, _} ->
1351+
ok
1352+
after timer:seconds(15) ->
1353+
ct:fail("Pid: ~p stuck in:~n~p~n",
1354+
[Pid, [process_info(dets_server:get_pid(Table), K) ||
1355+
K <- [monitored_by, current_stacktrace]]])
1356+
end.
1357+
12731358
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12741359

12751360

0 commit comments

Comments
 (0)