Skip to content

Commit

Permalink
Fix mnesia crash when checkpoint initialization fails
Browse files Browse the repository at this point in the history
If a table is deleted just when a checkpoint is starting,
the checkpoint creation could fail so that mnesia crashed.
  • Loading branch information
dgud committed Nov 20, 2024
1 parent 0418c10 commit de2261c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
30 changes: 17 additions & 13 deletions lib/mnesia/src/mnesia_checkpoint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -617,24 +617,28 @@ init(Cp) ->
Name = Cp#checkpoint_args.name,
Props = [set, public, {keypos, 2}],
try ?ets_new_table(mnesia_pending_checkpoint, Props) of
PendingTab ->
Rs = [prepare_tab(Cp, R) || R <- Cp#checkpoint_args.retainers],
Cp2 = Cp#checkpoint_args{retainers = Rs,
pid = self(),
pending_tab = PendingTab},
add(pending_checkpoint_pids, self()),
add(pending_checkpoints, PendingTab),
set({checkpoint, Name}, self()),
add(checkpoints, Name),
dbg_out("Checkpoint ~p (~p) started~n", [Name, self()]),
proc_lib:init_ack(Cp2#checkpoint_args.supervisor, {ok, self()}),
retainer_loop(Cp2)
PendingTab ->
try [prepare_tab(Cp, R) || R <- Cp#checkpoint_args.retainers] of
Rs ->
Cp2 = Cp#checkpoint_args{retainers = Rs,
pid = self(),
pending_tab = PendingTab},
add(pending_checkpoint_pids, self()),
add(pending_checkpoints, PendingTab),
set({checkpoint, Name}, self()),
add(checkpoints, Name),
dbg_out("Checkpoint ~p (~p) started~n", [Name, self()]),
proc_lib:init_ack(Cp2#checkpoint_args.supervisor, {ok, self()}),
retainer_loop(Cp2)
catch exit:Reason ->
proc_lib:init_ack(Cp#checkpoint_args.supervisor, {error, Reason})
end
catch error:Reason -> %% system limit
Msg = "Cannot create an ets table for pending transactions",
Error = {error, {system_limit, Name, Msg, Reason}},
proc_lib:init_ack(Cp#checkpoint_args.supervisor, Error)
end.

prepare_tab(Cp, R) ->
Tab = R#retainer.tab_name,
prepare_tab(Cp, R, val({Tab, storage_type})).
Expand Down
21 changes: 19 additions & 2 deletions lib/mnesia/test/mnesia_evil_coverage_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

-export([system_info/1, table_info/1, error_description/1,
db_node_lifecycle/1, evil_delete_db_node/1, start_and_stop/1,
checkpoint/1, table_lifecycle/1, storage_options/1,
checkpoint/1, checkpoint_del_table/1, table_lifecycle/1, storage_options/1,
add_copy_conflict/1, add_copy_when_going_down/1, add_copy_when_dst_going_down/1,
add_copy_with_down/1,
replica_management/1, clear_table_during_load/1,
Expand Down Expand Up @@ -64,7 +64,8 @@ end_per_testcase(Func, Conf) ->
all() ->
[system_info, table_info, error_description,
db_node_lifecycle, evil_delete_db_node, start_and_stop,
checkpoint, table_lifecycle, storage_options,
checkpoint, checkpoint_del_table,
table_lifecycle, storage_options,
add_copy_conflict,
add_copy_when_going_down, add_copy_when_dst_going_down, add_copy_with_down,
replica_management,
Expand Down Expand Up @@ -460,6 +461,22 @@ checkpoint(NodeConfig, Config) ->
lists:foreach(Fun, Tabs),
?verify_mnesia(TabNodes, []).


checkpoint_del_table(Config) when is_list(Config) ->
[Node1] = ?acquire_nodes(1, Config),
[mnesia:create_table(list_to_atom("a_" ++ integer_to_list(I)), []) || I <- lists:seq(1, 1000)],

Tabs = mnesia:system_info(local_tables),

spawn(fun() ->
mnesia:activate_checkpoint([{max, Tabs},{ram_overrides_dump, true}])
end),

{atomic, ok} = mnesia:delete_table(a_10),
%% Ensure we didn't crash

?verify_mnesia([Node1], []).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create and delete tables

Expand Down

0 comments on commit de2261c

Please sign in to comment.