Skip to content

Commit

Permalink
Make listener supervisor failures less painful
Browse files Browse the repository at this point in the history
Two general issues were addressed. The first one is the issue with
statically defined pids passed into childspecs. This issue prevents
regular supervisor' children restarts in the case of someone's
failure.

The second one is the not quite appropriate restart strategy.
Changed to rest_for_one which in pair with previous fixes assures
that live connections will not die in the case of partial failure.
Among possible failures are listening socket shutdown or frequent
accept errors.
  • Loading branch information
keynslug committed Dec 20, 2012
1 parent 7f4261d commit 8dad145
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 30 deletions.
14 changes: 7 additions & 7 deletions src/ranch_acceptors_sup.erl
Expand Up @@ -17,24 +17,24 @@
-behaviour(supervisor).

%% API.
-export([start_link/7]).
-export([start_link/5]).

%% supervisor.
-export([init/1]).

%% API.

-spec start_link(any(), non_neg_integer(), module(), any(),
module(), pid(), pid()) -> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts,
Protocol, ListenerPid, ConnsPid) ->
module()) -> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts, Protocol) ->
supervisor:start_link(?MODULE, [Ref, NbAcceptors, Transport, TransOpts,
Protocol, ListenerPid, ConnsPid]).
Protocol]).

%% supervisor.

init([Ref, NbAcceptors, Transport, TransOpts,
Protocol, ListenerPid, ConnsPid]) ->
init([Ref, NbAcceptors, Transport, TransOpts, Protocol]) ->
ListenerPid = ranch_server:lookup_listener(Ref),
ConnsPid = ranch_server:lookup_connections_sup(Ref),
LSocket = case proplists:get_value(socket, TransOpts) of
undefined ->
{ok, Socket} = Transport:listen(TransOpts),
Expand Down
11 changes: 6 additions & 5 deletions src/ranch_conns_sup.erl
Expand Up @@ -17,17 +17,17 @@
-behaviour(supervisor).

%% API.
-export([start_link/0]).
-export([start_link/1]).
-export([start_protocol/5]).

%% supervisor.
-export([init/1]).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
supervisor:start_link(?MODULE, []).
-spec start_link(any()) -> {ok, pid()}.
start_link(Ref) ->
supervisor:start_link(?MODULE, Ref).

-spec start_protocol(pid(), inet:socket(), module(), module(), any())
-> {ok, pid()}.
Expand All @@ -36,6 +36,7 @@ start_protocol(ListenerPid, Socket, Transport, Protocol, Opts) ->

%% supervisor.

init([]) ->
init(Ref) ->
ok = ranch_server:set_connections_sup(Ref, self()),
{ok, {{simple_one_for_one, 0, 1}, [{?MODULE, {?MODULE, start_protocol, []},
temporary, brutal_kill, worker, [?MODULE]}]}}.
1 change: 1 addition & 0 deletions src/ranch_listener.erl
Expand Up @@ -101,6 +101,7 @@ set_protocol_options(ServerPid, ProtoOpts) ->

%% @private
init([Ref, MaxConns, ProtoOpts]) ->
ok = ranch_server:insert_listener(Ref, self()),
{ok, #state{ref=Ref, max_conns=MaxConns, proto_opts=ProtoOpts}}.

%% @private
Expand Down
36 changes: 19 additions & 17 deletions src/ranch_listener_sup.erl
Expand Up @@ -28,23 +28,25 @@
-> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
{ok, SupPid} = supervisor:start_link(?MODULE, []),
{ok, ListenerPid} = supervisor:start_child(SupPid,
{ranch_listener, {ranch_listener, start_link,
[Ref, MaxConns, ProtoOpts]},
permanent, 5000, worker, [ranch_listener]}),
ok = ranch_server:insert_listener(Ref, ListenerPid),
{ok, ConnsPid} = supervisor:start_child(SupPid,
{ranch_conns_sup, {ranch_conns_sup, start_link, []},
permanent, 5000, supervisor, [ranch_conns_sup]}),
{ok, _PoolPid} = supervisor:start_child(SupPid,
{ranch_acceptors_sup, {ranch_acceptors_sup, start_link, [
Ref, NbAcceptors, Transport, TransOpts,
Protocol, ListenerPid, ConnsPid
]}, permanent, 5000, supervisor, [ranch_acceptors_sup]}),
{ok, SupPid}.
supervisor:start_link(?MODULE, {
Ref, NbAcceptors, MaxConns, Transport, TransOpts, Protocol, ProtoOpts
}).

%% supervisor.

init([]) ->
{ok, {{one_for_all, 10, 10}, []}}.
init({Ref, NbAcceptors, MaxConns, Transport, TransOpts, Protocol, ProtoOpts}) ->
ChildSpecs = [
%% listener
{ranch_listener, {ranch_listener, start_link,
[Ref, MaxConns, ProtoOpts]},
permanent, 5000, worker, [ranch_listener]},
%% conns_sup
{ranch_conns_sup, {ranch_conns_sup, start_link, [Ref]},
permanent, infinity, supervisor, [ranch_conns_sup]},
%% acceptors_sup
{ranch_acceptors_sup, {ranch_acceptors_sup, start_link,
[Ref, NbAcceptors, Transport, TransOpts, Protocol]
}, permanent, infinity, supervisor, [ranch_acceptors_sup]}
],
{ok, {{rest_for_one, 10, 10}, ChildSpecs}}.

15 changes: 14 additions & 1 deletion src/ranch_server.erl
Expand Up @@ -20,6 +20,8 @@
-export([start_link/0]).
-export([insert_listener/2]).
-export([lookup_listener/1]).
-export([set_connections_sup/2]).
-export([lookup_connections_sup/1]).
-export([add_acceptor/2]).
-export([send_to_acceptors/2]).
-export([add_connection/1]).
Expand Down Expand Up @@ -52,14 +54,25 @@ start_link() ->
%% @doc Insert a listener into the database.
-spec insert_listener(any(), pid()) -> ok.
insert_listener(Ref, Pid) ->
true = ets:insert_new(?TAB, {{listener, Ref}, Pid}),
true = ets:insert_new(?TAB, {{listener, Ref}, Pid, undefined}),
gen_server:cast(?MODULE, {insert_listener, Ref, Pid}).

%% @doc Lookup a listener in the database.
-spec lookup_listener(any()) -> pid().
lookup_listener(Ref) ->
ets:lookup_element(?TAB, {listener, Ref}, 2).

%% @doc Set a connection supervisor associated with specific listener.
-spec set_connections_sup(any(), pid()) -> ok.
set_connections_sup(Ref, Pid) ->
true = ets:update_element(?TAB, {listener, Ref}, {3, Pid}),
ok.

%% @doc Lookup a connection supervisor used by specific listener.
-spec lookup_connections_sup(any()) -> pid() | undefined.
lookup_connections_sup(Ref) ->
ets:lookup_element(?TAB, {listener, Ref}, 3).

%% @doc Add an acceptor for the given listener.
-spec add_acceptor(any(), pid()) -> ok.
add_acceptor(Ref, Pid) ->
Expand Down

0 comments on commit 8dad145

Please sign in to comment.