Add monitor dc_pool <-> down_conn

parent 15bff248
...@@ -35,9 +35,15 @@ ...@@ -35,9 +35,15 @@
-record(state, -record(state,
{dc_id :: mtp_config:dc_id(), {dc_id :: mtp_config:dc_id(),
%% This one might be really big:
upstreams = #{} :: #{upstream() => downstream()}, upstreams = #{} :: #{upstream() => downstream()},
%% On-demand downstreams are started asynchronously;
pending_downstreams = [] :: [pid()], pending_downstreams = [] :: [pid()],
downstreams :: ds_store() %% Downstream storage that allows to choose the one with minimal
%% number of connections
%% Should be relatively small
downstreams :: ds_store(),
downstream_monitors = #{} :: #{reference() => downstream()}
}). }).
%%%=================================================================== %%%===================================================================
...@@ -66,10 +72,11 @@ status(Pool) -> ...@@ -66,10 +72,11 @@ status(Pool) ->
%%%=================================================================== %%%===================================================================
init(DcId) -> init(DcId) ->
InitConnections = application:get_env(mtproto_proxy, init_dc_connections, 4), InitConnections = application:get_env(mtproto_proxy, init_dc_connections, 4),
PendingConnections = [do_connect(DcId) || _ <- lists:seq(1, InitConnections)], State = #state{dc_id = DcId,
Connections = recv_pending(PendingConnections), downstreams = ds_new([])},
Downstreams = ds_new(Connections), State1 = connect_many(InitConnections, State),
{ok, #state{dc_id = DcId, downstreams = Downstreams}}. State2 = wait_pending(State1),
{ok, State2}.
handle_call({get, Upstream, Opts}, _From, State) -> handle_call({get, Upstream, Opts}, _From, State) ->
{Downstream, State1} = handle_get(Upstream, Opts, State), {Downstream, State1} = handle_get(Upstream, Opts, State),
...@@ -140,17 +147,30 @@ handle_return(Upstream, #state{downstreams = Ds, ...@@ -140,17 +147,30 @@ handle_return(Upstream, #state{downstreams = Ds,
St#state{downstreams = Ds1, St#state{downstreams = Ds1,
upstreams = Us1}. upstreams = Us1}.
handle_down(MonRef, MaybeUpstream, #state{downstreams = Ds, handle_down(MonRef, Pid, #state{downstreams = Ds,
upstreams = Us} = St) -> downstream_monitors = DsM,
case maps:take(MaybeUpstream, Us) of upstreams = Us,
pending_downstreams = Pending} = St) ->
case maps:take(Pid, Us) of
{{Downstream, MonRef}, Us1} -> {{Downstream, MonRef}, Us1} ->
ok = mtp_down_conn:upstream_closed(Downstream, MaybeUpstream), ok = mtp_down_conn:upstream_closed(Downstream, Pid),
Ds1 = ds_return(Downstream, Ds), Ds1 = ds_return(Downstream, Ds),
St#state{downstreams = Ds1, St#state{downstreams = Ds1,
upstreams = Us1}; upstreams = Us1};
error -> error ->
lager:warning("Unexpected DOWN. ref=~p, pid=~p", [MonRef, MaybeUpstream]), case maps:take(MonRef, DsM) of
{Pid, DsM1} ->
Pending1 = lists:delete(Pid, Pending),
Ds1 = ds_remove(Pid, Ds),
lager:warning("Downstream=~p is down", [Pid]),
St#state{pending_downstreams = Pending1,
downstreams = Ds1,
downstream_monitors = DsM1};
_ ->
lager:warning("Unexpected DOWN. ref=~p, pid=~p",
[MonRef, Pid]),
St St
end
end. end.
maybe_spawn_connection(CurrentMin, #state{pending_downstreams = Pending} = St) -> maybe_spawn_connection(CurrentMin, #state{pending_downstreams = Pending} = St) ->
...@@ -167,18 +187,23 @@ maybe_spawn_connection(CurrentMin, #state{pending_downstreams = Pending} = St) - ...@@ -167,18 +187,23 @@ maybe_spawn_connection(CurrentMin, #state{pending_downstreams = Pending} = St) -
_ -> _ ->
0 0
end, end,
connect_many(ToSpawn, St).
connect_many(ToSpawn, St) ->
lists:foldl( lists:foldl(
fun(_, S) -> fun(_, S) ->
connect(S) connect(S)
end, St, lists:seq(1, ToSpawn)). end, St, lists:seq(1, ToSpawn)).
%% Initiate new async connection %% Initiate new async connection
connect(#state{pending_downstreams = Pending, connect(#state{pending_downstreams = Pending,
downstream_monitors = DsM,
dc_id = DcId} = St) -> dc_id = DcId} = St) ->
%% Should monitor connection PIDs as well! %% Should monitor connection PIDs as well!
Pid = do_connect(DcId), Pid = do_connect(DcId),
St#state{pending_downstreams = [Pid | Pending]}. MonRef = erlang:monitor(process, Pid),
St#state{pending_downstreams = [Pid | Pending],
downstream_monitors = DsM#{MonRef => Pid}}.
%% Asynchronous connect %% Asynchronous connect
do_connect(DcId) -> do_connect(DcId) ->
...@@ -186,12 +211,18 @@ do_connect(DcId) -> ...@@ -186,12 +211,18 @@ do_connect(DcId) ->
Pid. Pid.
%% Block until all async connections are acked %% Block until all async connections are acked
recv_pending(Pids) -> wait_pending(#state{pending_downstreams = Pending} = St) ->
[receive lists:foldl(
fun(Pid, #state{pending_downstreams = [Pid | Remaining],
downstreams = Ds} = St1) ->
receive
{'$gen_cast', {connected, Pid}} -> Pid {'$gen_cast', {connected, Pid}} -> Pid
after 10000 -> after 10000 ->
exit({timeout, receive Smth -> Smth after 0 -> none end}) exit({timeout, receive Smth -> Smth after 0 -> none end})
end || Pid <- Pids]. end,
St1#state{pending_downstreams = Remaining,
downstreams = ds_add_downstream(Pid, Ds)}
end, St, Pending).
%% New downstream connection storage %% New downstream connection storage
-spec ds_new([downstream()]) -> ds_store(). -spec ds_new([downstream()]) -> ds_store().
...@@ -228,3 +259,7 @@ ds_get(St) -> ...@@ -228,3 +259,7 @@ ds_get(St) ->
ds_return(Pid, St) -> ds_return(Pid, St) ->
{ok, St1} = pid_psq:dec_priority(Pid, St), {ok, St1} = pid_psq:dec_priority(Pid, St),
St1. St1.
-spec ds_remove(downstream(), ds_store()) -> ds_store().
ds_remove(Downstream, St) ->
pid_psq:delete(Downstream, St).
...@@ -129,9 +129,14 @@ code_change(_OldVsn, State, _Extra) -> ...@@ -129,9 +129,14 @@ code_change(_OldVsn, State, _Extra) ->
%% Send packet from upstream to downstream %% Send packet from upstream to downstream
handle_send(Data, Upstream, #state{upstreams = Ups, handle_send(Data, Upstream, #state{upstreams = Ups,
addr_bin = ProxyAddr} = St) -> addr_bin = ProxyAddr} = St) ->
UpstreamData = maps:get(Upstream, Ups), case Ups of
#{Upstream := UpstreamData} ->
Packet = mtp_rpc:encode_packet({data, Data}, {UpstreamData, ProxyAddr}), Packet = mtp_rpc:encode_packet({data, Data}, {UpstreamData, ProxyAddr}),
down_send(Packet, St). down_send(Packet, St);
_ ->
lager:warning("Upstream=~p not found", [Upstream]),
{{error, unknown_upstream}, St}
end.
%% New upstream connected %% New upstream connected
handle_upstream_new(Upstream, Opts, #state{upstreams = Ups, handle_upstream_new(Upstream, Opts, #state{upstreams = Ups,
...@@ -141,6 +146,7 @@ handle_upstream_new(Upstream, Opts, #state{upstreams = Ups, ...@@ -141,6 +146,7 @@ handle_upstream_new(Upstream, Opts, #state{upstreams = Ups,
AdTag = maps:get(ad_tag, Opts, undefined), AdTag = maps:get(ad_tag, Opts, undefined),
Ups1 = Ups#{Upstream => {ConnId, iolist_to_binary(mtp_rpc:encode_ip_port(Ip, Port)), AdTag}}, Ups1 = Ups#{Upstream => {ConnId, iolist_to_binary(mtp_rpc:encode_ip_port(Ip, Port)), AdTag}},
UpsRev1 = UpsRev#{ConnId => Upstream}, UpsRev1 = UpsRev#{ConnId => Upstream},
lager:debug("New upstream=~p conn_id=~p", [Upstream, ConnId]),
St#state{upstreams = Ups1, St#state{upstreams = Ups1,
upstreams_rev = UpsRev1}. upstreams_rev = UpsRev1}.
...@@ -231,9 +237,10 @@ up_send(Packet, ConnId, #state{upstreams_rev = UpsRev} = St) -> ...@@ -231,9 +237,10 @@ up_send(Packet, ConnId, #state{upstreams_rev = UpsRev} = St) ->
St; St;
error -> error ->
lager:warning("Unknown connection_id=~w", [ConnId]), lager:warning("Unknown connection_id=~w", [ConnId]),
ClosedPacket = mtp_rpc:encode_packet(remote_closed, ConnId), %% WHY!!!?
{ok, St1} = down_send(ClosedPacket, St), %% ClosedPacket = mtp_rpc:encode_packet(remote_closed, ConnId),
St1 %% {ok, St1} = down_send(ClosedPacket, St),
St
end. end.
connect(DcId, S) -> connect(DcId, S) ->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment