Skip to content

Commit 523bc97

Browse files
authored
Merge pull request #108 from zmstone/251122-ensure-cleanup-after-shutdown
fix: ensure cleanup after shutdown
2 parents 392237d + 10264de commit 523bc97

5 files changed

Lines changed: 83 additions & 53 deletions

File tree

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* 4.1.3
2+
- Ensure `wolff_client_sup:ensure_absence` and `wolff_producers_sup:ensure_absence` will perform shutdown and cleanup atomically.
3+
Previously, if the caller process is killed while waiting for shutdown, a terminated child may leak under the supervisor.
4+
15
* 4.1.2
26
- Made sure `wolff_client:check_topic_exists_with_client_pid/2` triggers topic creation when `allow_auto_topic_creation` is set to `true`.
37

src/wolff_client.erl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
%% but we need to find connection by {topic(), partition()}
5858
leaders => #{{topic(), partition()} => connection()},
5959
%% Reference counting so we may drop connection metadata when no longer required.
60-
known_topics := #{topic() => #{producer_group() => true}}
60+
known_topics := #{topic() => #{producer_group() => true}},
61+
owner := pid()
6162
}.
6263

6364
-define(DEFAULT_METADATA_TIMEOUT, 10000).
@@ -78,6 +79,7 @@
7879
start_link(ClientId, Hosts, Config) ->
7980
{ConnCfg0, MyCfg} = split_config(Config),
8081
ConnCfg = ConnCfg0#{client_id => ClientId},
82+
Owner = self(),
8183
St = #{client_id => ClientId,
8284
seed_hosts => Hosts,
8385
config => MyCfg,
@@ -86,7 +88,8 @@ start_link(ClientId, Hosts, Config) ->
8688
metadata_conn => not_initialized,
8789
metadata_ts => #{},
8890
leaders => #{},
89-
known_topics => #{}
91+
known_topics => #{},
92+
owner => Owner
9093
},
9194
case maps:get(reg_name, Config, false) of
9295
false -> gen_server:start_link(?MODULE, St, []);
@@ -199,7 +202,13 @@ handle_call(Call, _From, St) ->
199202
{reply, {error, {unknown_call, Call}}, St}.
200203

201204
handle_info({'EXIT', Pid, Reason}, St) ->
202-
{noreply, flush_exit_signals(St, Pid, Reason)};
205+
%% Check if this is a shutdown signal from the supervisor
206+
case erlang:whereis(wolff_client_sup) of
207+
Pid when Reason =:= shutdown ->
208+
{stop, shutdown, St};
209+
_ ->
210+
{noreply, flush_exit_signals(St, Pid, Reason)}
211+
end;
203212
handle_info(_Info, St) ->
204213
{noreply, upgrade(St)}.
205214

@@ -243,7 +252,7 @@ handle_cast(_Cast, St) ->
243252
code_change(_OldVsn, St, _Extra) ->
244253
{ok, St}.
245254

246-
terminate(_, #{client_id := ClientID, conns := Conns} = St) ->
255+
terminate(_Reason, #{client_id := ClientID, conns := Conns} = St) ->
247256
ok = wolff_client_sup:deregister_client(ClientID),
248257
MetadataConn = maps:get(metadata_conn, St, none),
249258
ok = close_connections(Conns),
@@ -307,9 +316,9 @@ close_connections(Conns, Topic) ->
307316
end,
308317
do_close_connections(maps:to_list(Conns), Pred, #{}).
309318

310-
flush_exit_signals(St0) ->
319+
flush_exit_signals(#{owner := Owner} = St0) ->
311320
receive
312-
{'EXIT', Pid, Reason} ->
321+
{'EXIT', Pid, Reason} when Pid =/= Owner ->
313322
flush_exit_signals(St0, Pid, Reason)
314323
after
315324
0 ->

src/wolff_client_sup.erl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ ensure_present(ClientId, Hosts, Config) ->
5050
%% @doc Ensure client stopped and deleted under supervisor.
5151
-spec ensure_absence(wolff:client_id()) -> ok.
5252
ensure_absence(ClientId) ->
53+
%% This receive may take 5s because the child spec's shutdown policy is to kill after 5s
54+
%% To ensure the cleanup is complete, spawn a process to do it, in case the caller
55+
%% gets killed while waiting for shutdown.
56+
{Pid, Mref} = erlang:spawn_monitor(fun() -> do_ensure_absence(ClientId) end),
57+
receive
58+
{'DOWN', Mref, process, Pid, _} ->
59+
ok
60+
end.
61+
62+
do_ensure_absence(ClientId) ->
5363
case supervisor:terminate_child(?SUPERVISOR, ClientId) of
5464
ok -> ok = supervisor:delete_child(?SUPERVISOR, ClientId);
5565
{error, not_found} -> ok
@@ -79,7 +89,8 @@ child_spec(ClientId, Hosts, Config) ->
7989
start => {wolff_client, start_link, [ClientId, Hosts, Config]},
8090
restart => transient,
8191
type => worker,
82-
modules => [wolff_client]
92+
modules => [wolff_client],
93+
shutdown => 5000
8394
}.
8495

8596
%% @doc Create a ets table which is used for client registration.

src/wolff_producers_sup.erl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ ensure_present(ClientId, ProducerId, Config) ->
4848
%% ensure client stopped and deleted under supervisor
4949
-spec ensure_absence(wolff_producers:id()) -> ok.
5050
ensure_absence(ProducerId) ->
51+
%% This receive may take 5s because the child spec's shutdown policy is to kill after 5s
52+
%% To ensure the cleanup is complete, spawn a process to do it, in case the caller
53+
%% gets killed while waiting for shutdown.
54+
{Pid, Mref} = erlang:spawn_monitor(fun() -> do_ensure_absence(ProducerId) end),
55+
receive
56+
{'DOWN', Mref, process, Pid, _} ->
57+
ok
58+
end.
59+
60+
do_ensure_absence(ProducerId) ->
5161
case supervisor:terminate_child(?SUPERVISOR, ProducerId) of
5262
ok ->
5363
ok = wolff_producers:cleanup_workers_table(ProducerId),
@@ -60,7 +70,9 @@ child_spec(ClientId, ProducerId, Config) ->
6070
#{id => ProducerId,
6171
start => {wolff_producers, start_link, [ClientId, ProducerId, Config]},
6272
restart => transient,
63-
type => worker
73+
type => worker,
74+
modules => [wolff_producers],
75+
shutdown => 5000
6476
}.
6577

6678
%% Find the running process (gen_server of wolff_producers) from thie child ID.

0 commit comments

Comments
 (0)