Current section

Files

Jump to
exq lib exq heartbeat monitor.ex
Raw

lib/exq/heartbeat/monitor.ex

defmodule Exq.Heartbeat.Monitor do
use GenServer
require Logger
alias Exq.Redis.Heartbeat
alias Exq.Support.Config
defmodule State do
defstruct [
:namespace,
:redis,
:interval,
:queues,
:node_id,
:missed_heartbeats_allowed,
:stats
]
end
def start_link(options) do
GenServer.start_link(
__MODULE__,
%State{
namespace: Keyword.fetch!(options, :namespace),
redis: Keyword.fetch!(options, :redis),
interval: Keyword.fetch!(options, :heartbeat_interval),
node_id: Keyword.get(options, :node_id, Config.node_identifier().node_id()),
queues: Keyword.fetch!(options, :queues),
stats: Keyword.get(options, :stats),
missed_heartbeats_allowed: Keyword.fetch!(options, :missed_heartbeats_allowed)
},
[]
)
end
def init(state) do
:ok = schedule_verify(state.interval)
{:ok, state}
end
def handle_info(:verify, state) do
case Heartbeat.dead_nodes(
state.redis,
state.namespace,
state.interval,
state.missed_heartbeats_allowed
) do
{:ok, node_ids} ->
Enum.each(node_ids, fn {node_id, score} ->
:ok = re_enqueue_backup(state, node_id, score)
end)
_error ->
:ok
end
:ok = schedule_verify(state.interval)
{:noreply, state}
end
def handle_info(msg, state) do
Logger.error("Received unexpected info message in #{__MODULE__} #{inspect(msg)}")
{:noreply, state}
end
defp schedule_verify(interval) do
_reference = Process.send_after(self(), :verify, interval)
:ok
end
defp re_enqueue_backup(state, node_id, score) do
Logger.info(
"#{node_id} missed the last #{state.missed_heartbeats_allowed} heartbeats. Re-enqueing jobs from backup and cleaning up stats."
)
Enum.uniq(Exq.Redis.JobQueue.list_queues(state.redis, state.namespace) ++ state.queues)
|> Enum.each(fn queue ->
Heartbeat.re_enqueue_backup(state.redis, state.namespace, node_id, queue, score)
end)
if state.stats do
:ok = Exq.Stats.Server.cleanup_host_stats(state.stats, state.namespace, node_id)
end
_ = Heartbeat.unregister(state.redis, state.namespace, node_id)
:ok
end
end