app: use normal assigns instead of streams for the server list

This commit is contained in:
Nikos Papadakis 2023-07-26 22:41:52 +03:00
parent dbdc7e0d80
commit 90ccdedd7b
Signed by untrusted user who does not match committer: nikos
GPG key ID: 78871F9905ADFF02
6 changed files with 105 additions and 113 deletions

View file

@ -18,8 +18,8 @@ defmodule Prymn.Agents do
Returns `{:error, reason}` when the connection could not be established
"""
@spec ensure_connection(String.t()) :: :ok | {:error, term}
def ensure_connection(address) do
child = {Prymn.Agents.Connection, address}
def ensure_connection(public_ip) do
child = {Prymn.Agents.Connection, public_ip}
case DynamicSupervisor.start_child(Prymn.Agents.Supervisor, child) do
{:ok, _pid} -> :ok

View file

@ -6,11 +6,12 @@ defmodule Prymn.Agents.Connection do
defstruct [:channel, up?: false]
@ping_interval 20000
use GenServer, restart: :transient
@healthcheck_inverval 20000
require Logger
alias PrymnProto.Prymn.Agent.Stub, as: Grpc
use GenServer, restart: :transient
@spec start_link(String.t()) :: GenServer.on_start()
def start_link(addr) do
@ -28,17 +29,16 @@ defmodule Prymn.Agents.Connection do
end
@impl true
def init(addr) do
case GRPC.Stub.connect(addr) do
def init(public_ip) do
case GRPC.Stub.connect("#{public_ip}:50012") do
{:ok, channel} ->
Logger.info("Starting new connection at address #{addr}")
send(self(), :do_healthcheck)
state = %__MODULE__{channel: channel, up?: true}
Process.send_after(self(), :do_healthcheck, @ping_interval)
{:ok, state}
{:ok, %__MODULE__{channel: channel, up?: true}}
{:error, error} ->
broadcast_healthcheck!(:down, public_ip)
{:stop, error}
end
end
@ -50,50 +50,41 @@ defmodule Prymn.Agents.Connection do
@impl true
def handle_info({:gun_up, _pid, _protocol}, %{channel: channel} = state) do
Logger.info("[Agent] #{state.channel.host} regained connection")
Phoenix.PubSub.broadcast!(
Prymn.PubSub,
"agent:#{channel.host}",
{:healthcheck, :up, channel.host}
)
broadcast_healthcheck!(:up, channel.host)
{:noreply, %{state | up?: true}}
end
@impl true
def handle_info({:gun_down, _pid, _proto, reason, _}, %{channel: channel} = state) do
Logger.info("[Agent] #{channel.host} lost connection, reason: #{reason}")
Phoenix.PubSub.broadcast!(
Prymn.PubSub,
"agent:#{channel.host}",
{:healthcheck, :down, channel.host}
)
def handle_info({:gun_down, _pid, _proto, _reason, _}, %{channel: channel} = state) do
broadcast_healthcheck!(:down, channel.host)
{:noreply, %{state | up?: false}}
end
@impl true
def handle_info(:do_healthcheck, %{channel: channel} = state) do
def handle_info(:do_healthcheck, %{channel: channel, up?: up?} = state) do
request = %PrymnProto.Prymn.EchoRequest{message: "hello"}
case PrymnProto.Prymn.Agent.Stub.echo(channel, request) do
if up? do
case Grpc.echo(channel, request) do
{:ok, _reply} ->
:noop
broadcast_healthcheck!(:up, channel.host)
{:error, error} ->
Logger.warning("healthcheck error for server #{channel.host}, reason: #{inspect(error)}")
Logger.warning(
"healthcheck error for server #{channel.host}, reason: #{inspect(error)}"
)
end
else
broadcast_healthcheck!(:down, channel.host)
end
Process.send_after(self(), :do_healthcheck, @ping_interval)
Process.send_after(self(), :do_healthcheck, @healthcheck_inverval)
{:noreply, state}
end
@impl true
def handle_info(msg, state) do
Logger.info("received unexpected message: #{inspect(msg)}")
Logger.debug("received unexpected message: #{inspect(msg)}")
{:noreply, state}
end
@ -105,4 +96,12 @@ defmodule Prymn.Agents.Connection do
defp via(name) do
{:via, Registry, {Prymn.Agents.Registry, name}}
end
defp broadcast_healthcheck!(msg, ip_address) do
Phoenix.PubSub.broadcast!(
Prymn.PubSub,
"agent:#{ip_address}",
{:healthcheck, ip_address, msg}
)
end
end

View file

@ -18,7 +18,7 @@ defmodule Prymn.Servers do
"""
def list_servers do
Repo.all(Server)
Repo.all(Server |> order_by(desc: :inserted_at))
end
@doc """

View file

@ -436,8 +436,6 @@ defmodule PrymnWeb.CoreComponents do
attr :rows, :list, required: true
attr :row_id, :any, default: nil, doc: "the function for generating the row id"
attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
attr :row_indicator, :any, default: nil
attr :indicator_label, :string, default: "Indicator"
attr :row_item, :any,
default: &Function.identity/1,
@ -460,11 +458,16 @@ defmodule PrymnWeb.CoreComponents do
<table class="w-[40rem] mt-11 sm:w-full">
<thead class="text-left text-sm leading-6 text-zinc-500">
<tr>
<th class="p-0 pb-4 font-normal">
<div class="w-min"><%= @indicator_label %></div>
</th>
<th :for={col <- @col} class="p-0 pr-6 pb-4 font-normal"><%= col[:label] %></th>
<th class="relative p-0 pb-4"><span class="sr-only"><%= gettext("Actions") %></span></th>
<th class="relative p-0 pb-4">
<span class="sr-only">
<%= if @gettext do %>
<%= gettext("Actions") %>
<% else %>
Actions
<% end %>
</span>
</th>
</tr>
</thead>
<tbody
@ -473,21 +476,13 @@ defmodule PrymnWeb.CoreComponents do
class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
>
<tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
<td
class="relative w-min p-0 hover:cursor-pointer"
phx-click={@row_click && @row_click.(row)}
>
<div class="block py-4 pr-6">
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
<span class="relative hover:cursor-pointer"><%= @row_indicator.(row) %></span>
</div>
</td>
<td
:for={{col, i} <- Enum.with_index(@col)}
phx-click={@row_click && @row_click.(row)}
class={["relative p-0", @row_click && "hover:cursor-pointer"]}
>
<div class="block py-4 pr-6">
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
<%= render_slot(col, @row_item.(row)) %>
</span>

View file

@ -1,25 +1,26 @@
defmodule PrymnWeb.ServerLive.Index do
alias Prymn.Servers
require Logger
alias Prymn.{Servers, Agents}
use PrymnWeb, :live_view
@impl true
def mount(_params, _session, socket) do
servers = Servers.list_servers()
pid = self()
# pid = self()
for %{public_ip: public_ip} <- servers, public_ip != nil do
:ok = Phoenix.PubSub.subscribe(Prymn.PubSub, "agent:#{public_ip}")
for %Servers.Server{status: :registered, public_ip: ip} <- servers do
:ok = Phoenix.PubSub.subscribe(Prymn.PubSub, "agent:#{ip}")
Task.start_link(fn ->
case Prymn.Agents.ensure_connection("#{public_ip}:50012") do
:ok -> Process.send(pid, {:healthcheck, :up, public_ip}, [])
{:error, _error} -> Process.send(pid, {:healthcheck, :down, public_ip}, [])
case Agents.ensure_connection(ip) do
:ok -> IO.puts("Ok")
_ -> IO.puts("not ok")
end
end)
end
{:ok, stream(socket, :servers, servers)}
{:ok, assign(socket, :servers, servers)}
end
@impl true
@ -39,29 +40,34 @@ defmodule PrymnWeb.ServerLive.Index do
@impl true
def handle_info({:connect, %Servers.Server{} = server}, socket) do
{:noreply, stream_insert(socket, :servers, server)}
end
@impl true
def handle_info({:healthcheck, status, ip}, socket) do
server = Servers.get_server_by_ip!(ip)
status =
case status do
:up -> "Connected"
:down -> "Disconnected"
end
{:noreply,
socket
|> stream_insert(:servers, Map.put(server, :connection_status, status))}
|> update(:servers, fn servers -> [server | servers] end)}
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
server = Servers.get_server!(id)
{:ok, _} = Servers.delete_server(server)
def handle_info({:healthcheck, ip, _message}, socket) do
servers =
update_in(
socket.assigns.servers,
[Access.filter(&match?(%{public_ip: ^ip}, &1))],
&Map.merge(&1, %{connection_status: "Connected"})
)
{:noreply, stream_delete(socket, :servers, server)}
{:noreply, assign(socket, :servers, servers)}
end
@impl true
def handle_info(msg, state) do
Logger.debug("received unexpected message #{inspect(msg)}")
{:noreply, state}
end
# @impl true
# def handle_event("delete", %{"id" => id}, socket) do
# server = Servers.get_server!(id)
# {:ok, _} = Servers.delete_server(server)
# {:noreply, stream_delete(socket, :servers, server)}
# end
end

View file

@ -1,5 +1,8 @@
<.header>
All available servers to you
Your servers
<small class="block">
<%= "#{Enum.count(@servers)} servers" %>
</small>
<:actions>
<.link patch={~p"/servers/new"}>
<.button>Connect a Server</.button>
@ -7,38 +10,27 @@
</:actions>
</.header>
<.table
id="servers"
rows={@streams.servers}
row_click={fn {_id, server} -> JS.navigate(~p"/servers/#{server}") end}
row_indicator={
fn
{_id, %Servers.Server{status: :unregistered}} ->
~H(<span class="text-grey-600">Awaiting registration</span>)
{_id, %Servers.Server{connection_status: nil, status: :registered}} ->
~H(<span class="text-yellow-600">Connecting...</span>)
{_id, %Servers.Server{connection_status: "Connected"}} ->
~H(<span class="text-green-600">Connected</span>)
{_id, %Servers.Server{connection_status: "Disconnected"}} ->
~H(<span class="text-red-600">Disconnected</span>)
end
}
indicator_label="Status"
>
<:col :let={{_id, server}} label="Name"><%= server.name %></:col>
<:col :let={{_id, server}} label="IP"><%= server.public_ip || "N/A" %></:col>
<:action :let={{id, server}}>
<div class="space-y-5" phx-update="replace" id="servers">
<.link
phx-click={JS.push("delete", value: %{id: server.id}) |> hide("##{id}")}
data-confirm="Are you sure?"
:for={server <- @servers}
navigate={~p"/servers/#{server}"}
class="group block rounded-lg bg-gray-100 p-5 shadow-sm shadow-gray-300 hover:bg-black hover:text-white"
>
Delete
<div class="flex flex-row flex-wrap justify-between">
<h2 class="text-xl"><%= server.name %></h2>
<span class="self-center text-sm">
<%= if server.status == :registered do %>
<%= server.connection_status || "..." %>
<% else %>
Not registered
<% end %>
</span>
</div>
<div class="lg:text-sm">
<span>IP: <%= server.public_ip || "N/A" %></span>
</div>
</.link>
</:action>
</.table>
</div>
<.modal :if={@live_action == :new} id="server-modal" show on_cancel={JS.patch(~p"/servers")}>
<.live_component module={PrymnWeb.ServerLive.NewServer} id={:new} patch={~p"/servers"} />