2023-06-27 19:28:00 +00:00
|
|
|
defmodule Prymn.Agents.Connection do
|
|
|
|
@moduledoc false
|
|
|
|
|
2023-07-09 16:41:41 +00:00
|
|
|
# TODO: Disconnect after a while of idling. Disconnect when the healthcheck
|
|
|
|
# fails too many times.
|
|
|
|
|
2023-06-27 19:28:00 +00:00
|
|
|
defstruct [:channel, up?: false]
|
|
|
|
|
2023-07-26 19:41:52 +00:00
|
|
|
@healthcheck_inverval 20000
|
2023-06-27 19:28:00 +00:00
|
|
|
|
|
|
|
require Logger
|
2023-07-26 19:41:52 +00:00
|
|
|
alias PrymnProto.Prymn.Agent.Stub, as: Grpc
|
|
|
|
|
|
|
|
use GenServer, restart: :transient
|
2023-06-27 19:28:00 +00:00
|
|
|
|
|
|
|
@spec start_link(String.t()) :: GenServer.on_start()
|
|
|
|
def start_link(addr) do
|
|
|
|
GenServer.start_link(__MODULE__, addr, name: via(addr))
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec get_channel(String.t()) :: GRPC.Channel.t()
|
|
|
|
def get_channel(addr) do
|
|
|
|
GenServer.call(via(addr), :get_channel)
|
|
|
|
end
|
|
|
|
|
|
|
|
@spec drop(String.t()) :: :ok
|
|
|
|
def drop(addr) do
|
|
|
|
GenServer.stop(via(addr), :shutdown)
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-07-26 19:41:52 +00:00
|
|
|
def init(public_ip) do
|
|
|
|
case GRPC.Stub.connect("#{public_ip}:50012") do
|
2023-06-27 19:28:00 +00:00
|
|
|
{:ok, channel} ->
|
2023-07-26 19:41:52 +00:00
|
|
|
send(self(), :do_healthcheck)
|
2023-06-27 19:28:00 +00:00
|
|
|
|
2023-07-26 19:41:52 +00:00
|
|
|
{:ok, %__MODULE__{channel: channel, up?: true}}
|
2023-06-27 19:28:00 +00:00
|
|
|
|
|
|
|
{:error, error} ->
|
2023-07-26 19:41:52 +00:00
|
|
|
broadcast_healthcheck!(:down, public_ip)
|
|
|
|
|
2023-06-27 19:28:00 +00:00
|
|
|
{:stop, error}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_call(:get_channel, _from, state) do
|
|
|
|
{:reply, state.channel, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-07-09 16:41:41 +00:00
|
|
|
def handle_info({:gun_up, _pid, _protocol}, %{channel: channel} = state) do
|
2023-07-26 19:41:52 +00:00
|
|
|
broadcast_healthcheck!(:up, channel.host)
|
2023-06-27 19:28:00 +00:00
|
|
|
{:noreply, %{state | up?: true}}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-07-26 19:41:52 +00:00
|
|
|
def handle_info({:gun_down, _pid, _proto, _reason, _}, %{channel: channel} = state) do
|
|
|
|
broadcast_healthcheck!(:down, channel.host)
|
2023-06-27 19:28:00 +00:00
|
|
|
{:noreply, %{state | up?: false}}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-07-26 19:41:52 +00:00
|
|
|
def handle_info(:do_healthcheck, %{channel: channel, up?: up?} = state) do
|
2023-06-27 19:28:00 +00:00
|
|
|
request = %PrymnProto.Prymn.EchoRequest{message: "hello"}
|
|
|
|
|
2023-07-26 19:41:52 +00:00
|
|
|
if up? do
|
|
|
|
case Grpc.echo(channel, request) do
|
|
|
|
{:ok, _reply} ->
|
|
|
|
broadcast_healthcheck!(:up, channel.host)
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
Logger.warning(
|
|
|
|
"healthcheck error for server #{channel.host}, reason: #{inspect(error)}"
|
|
|
|
)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
broadcast_healthcheck!(:down, channel.host)
|
2023-06-27 19:28:00 +00:00
|
|
|
end
|
|
|
|
|
2023-07-26 19:41:52 +00:00
|
|
|
Process.send_after(self(), :do_healthcheck, @healthcheck_inverval)
|
2023-06-27 19:28:00 +00:00
|
|
|
{:noreply, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_info(msg, state) do
|
2023-07-26 19:41:52 +00:00
|
|
|
Logger.debug("received unexpected message: #{inspect(msg)}")
|
2023-06-27 19:28:00 +00:00
|
|
|
{:noreply, state}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def terminate(_reason, %{channel: channel}) do
|
|
|
|
GRPC.Stub.disconnect(channel)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp via(name) do
|
|
|
|
{:via, Registry, {Prymn.Agents.Registry, name}}
|
|
|
|
end
|
2023-07-26 19:41:52 +00:00
|
|
|
|
|
|
|
defp broadcast_healthcheck!(msg, ip_address) do
|
|
|
|
Phoenix.PubSub.broadcast!(
|
|
|
|
Prymn.PubSub,
|
|
|
|
"agent:#{ip_address}",
|
|
|
|
{:healthcheck, ip_address, msg}
|
|
|
|
)
|
|
|
|
end
|
2023-06-27 19:28:00 +00:00
|
|
|
end
|