File 0779-Fix-del_table_copy-loading-loop.patch of Package erlang
From 89f849fbe14e89a7578c0513cfddeab3f5dbfc87 Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Wed, 24 Apr 2024 10:54:59 +0200
Subject: [PATCH] Fix del_table_copy loading loop
When a local table copy was deleted but not yet loaded mnesia could
try to load it in a loop, which later when a table copy was added
again could result in a crash, since we don't have locks on the
receiver anymore. Tables can be simultaneously loaded and added.
Fixed by removing the table from the loader queue when it is
determined that the local copy is removed.
Also don't 'fatal' crash mnesia if table couldn't be loaded due
to table already existing.
---
lib/mnesia/src/mnesia_controller.erl | 29 +++++++++++++---------
lib/mnesia/src/mnesia_event.erl | 11 +++++----
lib/mnesia/src/mnesia_loader.erl | 36 ++++++++++++++++++----------
3 files changed, 48 insertions(+), 28 deletions(-)
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index f19960360f..1e38be6ba7 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1121,6 +1121,10 @@ handle_cast({adopt_orphans, Node, Tabs}, State) ->
end,
noreply(State2);
+handle_cast({del_table_copy, Tab}, #state{late_loader_queue = LLQ0, loader_queue = LQ0} = State0) ->
+ noreply(State0#state{late_loader_queue = gb_trees:delete_any(Tab, LLQ0),
+ loader_queue = gb_trees:delete_any(Tab, LQ0)});
+
handle_cast(Msg, State) ->
error("~p got unexpected cast: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State).
@@ -1224,19 +1228,19 @@ handle_info(Done = #loader_done{worker_pid=WPid, table_name=Tab}, State0) ->
false ->
ignore
end,
- case ?catch_val({Tab, active_replicas}) of
- [_|_] -> % still available elsewhere
+
+ case {?catch_val({Tab, storage_type}), val({Tab, active_replicas})} of
+ {unknown, _} -> %% Should not have a local copy anymore
+ State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)};
+ {_, [_|_]} -> % still available elsewhere
{value,{_,Worker}} = lists:keysearch(WPid,1,get_loaders(State0)),
add_loader(Tab,Worker,State1);
- _ ->
+ {ram_copies, []} ->
DelState = State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)},
- case ?catch_val({Tab, storage_type}) of
- ram_copies ->
- cast({disc_load, Tab, ram_only}),
- DelState;
- _ ->
- DelState
- end
+ cast({disc_load, Tab, ram_only}),
+ DelState;
+ {_, []} -> %% Table deleted or not loaded anywhere
+ State1#state{late_loader_queue=gb_trees:delete_any(Tab, LateQueue0)}
end
end,
State3 = opt_start_worker(State2),
@@ -1764,6 +1768,10 @@ del_active_replica(Tab, Node) ->
set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit
mnesia_lib:del({Tab, active_replicas}, Node),
mnesia_lib:del({Tab, where_to_write}, Node),
+ case Node =:= node() of
+ true -> cast({del_table_copy, Tab});
+ false -> ok
+ end,
update_where_to_wlock(Tab).
change_table_access_mode(Cs) ->
@@ -2098,7 +2106,6 @@ opt_start_loader(State = #state{loader_queue = LoaderQ}) ->
true ->
opt_start_loader(State#state{loader_queue = Rest});
false ->
- %% Start worker but keep him in the queue
Pid = load_and_reply(self(), Worker),
State#state{loader_pid=[{Pid,Worker}|get_loaders(State)],
loader_queue = Rest}
diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl
index 282c2c2c15..f89ecbadb1 100644
--- a/lib/mnesia/src/mnesia_event.erl
+++ b/lib/mnesia/src/mnesia_event.erl
@@ -109,7 +109,7 @@ handle_any_event(Msg, State) ->
{ok, State}.
handle_table_event({Oper, Record, TransId}, State) ->
- report_info("~p performed by ~p on record:~n\t~tp~n",
+ report_info("~p performed by ~p on record:~n\t~0tp~n",
[Oper, TransId, Record]),
{ok, State}.
@@ -160,9 +160,12 @@ handle_system_event({mnesia_overload, Details}, State) ->
report_warning("Mnesia is overloaded: ~tw~n", [Details]),
{ok, State};
-handle_system_event({mnesia_info, Format, Args}, State) ->
- report_info(Format, Args),
- {ok, State};
+handle_system_event({mnesia_info, Format, Args} = Event, State) ->
+ case put(last, Event) of
+ Event -> ok;
+ _ -> report_info(Format, Args)
+ end,
+ {ok, State};
handle_system_event({mnesia_warning, Format, Args}, State) ->
report_warning(Format, Args),
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index 1a65dac222..827ca46c2b 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -47,7 +47,7 @@ val(Var) ->
disc_load_table(Tab, Reason, Cs) ->
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
Type = val({Tab, setorbag}),
- dbg_out("Getting table ~tp (~p) from disc: ~tp~n",
+ dbg_out("Getting table ~0tp (~0p) from disc: ~0tp~n",
[Tab, Storage, Reason]),
?eval_debug_fun({?MODULE, do_get_disc_copy},
[{tab, Tab},
@@ -56,9 +56,9 @@ disc_load_table(Tab, Reason, Cs) ->
{type, Type}]),
do_get_disc_copy2(Tab, Reason, Storage, Type).
-do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown ->
- verbose("Local table copy of ~tp has recently been deleted, ignored.~n",
- [Tab]),
+do_get_disc_copy2(Tab, Reason, Storage, _Type) when Storage == unknown ->
+ verbose("Local table copy of ~0tp ~0p has recently been deleted, ignored.~n",
+ [Tab, Reason]),
{not_loaded, storage_unknown};
do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies ->
%% NOW we create the actual table
@@ -206,14 +206,14 @@ try_net_load_table(Tab, Reason, Ns, Cs) ->
end,
do_get_network_copy(Tab, Reason, Ns, Storage, Cs).
-do_get_network_copy(Tab, _Reason, _Ns, unknown, _Cs) ->
- verbose("Local table copy of ~tp has recently been deleted, ignored.~n", [Tab]),
+do_get_network_copy(Tab, Reason, _Ns, unknown, _Cs) ->
+ verbose("Local table copy of ~0tp (~0p) has recently been deleted, ignored.~n", [Tab,Reason]),
{not_loaded, storage_unknown};
do_get_network_copy(Tab, Reason, Ns, Storage, Cs) ->
[Node | Tail] = Ns,
case lists:member(Node,val({current, db_nodes})) of
true ->
- dbg_out("Getting table ~tp (~p) from node ~p: ~tp~n",
+ dbg_out("Getting table ~0tp (~0p) from node ~0p: ~0tp~n",
[Tab, Storage, Node, Reason]),
?eval_debug_fun({?MODULE, do_get_network_copy},
[{tab, Tab}, {reason, Reason},
@@ -289,6 +289,14 @@ init_receiver(Node, Tab,Storage,Cs,Reason) ->
{atomic, {error,Result}} when
element(1,Reason) == dumper ->
{error,Result};
+ {atomic, {error,{mktab, _} = Reason}} ->
+ case val({Tab,where_to_read}) == node() of
+ true -> %% Already loaded
+ ok;
+ false ->
+ fatal("Cannot create table ~tp: ~tp~n",
+ [[Tab, Storage], Reason])
+ end;
{atomic, {error,Result}} ->
fatal("Cannot create table ~tp: ~tp~n",
[[Tab, Storage], Result]);
@@ -415,9 +423,9 @@ create_table(Tab, TabSize, Storage, Cs) ->
{ok, _} ->
mnesia_lib:unlock_table(Tab),
{Storage, Tab};
- Else ->
+ {error, Reason} ->
mnesia_lib:unlock_table(Tab),
- Else
+ {error, {mktab, Reason}}
end;
(Storage == ram_copies) or (Storage == disc_copies) ->
EtsOpts = proplists:get_value(ets, StorageProps, []),
@@ -425,16 +433,18 @@ create_table(Tab, TabSize, Storage, Cs) ->
case mnesia_monitor:unsafe_mktab(Tab, Args) of
Tab ->
{Storage, Tab};
- Else ->
- Else
+ {error, Reason} ->
+ {error, {mktab, Reason}}
end;
element(1, Storage) == ext ->
{_, Alias, Mod} = Storage,
case mnesia_monitor:unsafe_create_external(Tab, Alias, Mod, Cs) of
ok ->
{Storage, Tab};
- Else ->
- Else
+ {error, Reason} ->
+ {error, {mktab, Reason}};
+ Reason ->
+ {error, {mktab, Reason}}
end
end.
--
2.35.3