1
0
Fork 0
mirror of https://github.com/benbusby/farside.git synced 2025-06-08 02:16:36 +00:00
farside/lib/farside/instance.ex

124 lines
2.6 KiB
Elixir

defmodule Farside.Instance do
use GenServer
require Logger
@registry_name :servers
def child_spec(args) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [args]},
type: :worker
}
end
def init(init_arg) do
ref =
:ets.new(String.to_atom(init_arg.type), [
:set,
:named_table,
:public,
read_concurrency: true,
write_concurrency: true
])
:ets.insert(ref, {:data, init_arg})
{:ok, %{type: init_arg.type, ref: ref}}
end
def start_link(arg) do
name = via_tuple(arg.type)
GenServer.start_link(__MODULE__, arg, name: name)
end
def shutdown() do
GenServer.call(__MODULE__, :shutdown)
end
def handle_call(
:shutdown,
_from,
state
) do
{:stop, {:ok, "Normal Shutdown"}, state}
end
def handle_cast(
:shutdown,
state
) do
{:stop, :normal, state}
end
def handle_cast(
:update,
state
) do
service = :ets.lookup(String.to_atom(state.type), :data)
{_, service} = List.first(service)
queries = Application.fetch_env!(:farside, :queries)
request_urls =
Enum.map(service.instances, fn x ->
x <>
EEx.eval_string(
service.test_url,
query: Enum.random(queries)
)
end)
tasks =
for request_url <- request_urls do
Task.async(fn ->
reply = Farside.Http.request(request_url, service.type)
{request_url, reply}
end)
end
tasks_with_results = Task.yield_many(tasks, 5000)
instances =
Enum.map(tasks_with_results, fn {task, res} ->
# Shut down the tasks that did not reply nor exit
res || Task.shutdown(task, :brutal_kill)
end)
|> Enum.reject(fn x -> x == nil end)
|> Enum.map(fn {_, value} -> value end)
|> Enum.filter(fn {instance_url, value} ->
value == :good
end)
|> Enum.map(fn {url, _} -> url end)
values = %{service | instances: instances}
:ets.delete_all_objects(String.to_atom(state.type))
:ets.insert(state.ref, {:data, values})
update_file = Application.fetch_env!(:farside, :update_file)
File.write(update_file, values.fallback)
{:noreply, state}
end
@doc false
def via_tuple(data, registry \\ @registry_name) do
{:via, Registry, {registry, data}}
end
@impl true
def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
:ets.delete(names)
{:noreply, {names, refs}}
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
end