Packages
honeydew
1.0.0-rc6
1.5.0
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc7
1.0.0-rc6
1.0.0-rc5
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Pluggable local/clusterable job queue focused on safety.
Current section
Files
Jump to
Current section
Files
lib/honeydew/worker.ex
defmodule Honeydew.Worker do
use GenServer
require Logger
require Honeydew
alias Honeydew.Job
defmodule State do
defstruct [:queue, :module, :user_state]
end
def start_link(queue, module, args, init_retry_secs) do
GenServer.start_link(__MODULE__, [queue, module, args, init_retry_secs])
end
def init([queue, module, args, init_retry_secs]) do
Process.flag(:trap_exit, true)
state = %State{queue: queue, module: module}
module.__info__(:functions)
|> Enum.member?({:init, 1})
|> worker_init(args, state)
|> case do
{:ok, state} ->
queue
|> Honeydew.group(:workers)
|> :pg2.join(self())
GenServer.cast(self(), :subscribe_to_queues)
{:ok, state}
bad ->
Logger.warn("#{module}.init/1 must return {:ok, state}, got: #{inspect bad}, retrying in #{init_retry_secs}s...")
:timer.apply_after(init_retry_secs * 1_000, Supervisor, :start_child, [Honeydew.supervisor(queue, :worker), []])
:ignore
end
end
def worker_init(true, args, %State{module: module} = state) do
try do
case apply(module, :init, [args]) do
{:ok, user_state} ->
{:ok, %{state | user_state: {:state, user_state}}}
bad ->
{:error, bad}
end
rescue e ->
{:exception, e}
end
end
def worker_init(false, _args, state), do: {:ok, %{state | user_state: :no_state}}
def handle_cast({:run, %Job{task: task, from: from, monitor: monitor} = job}, %State{queue: queue, module: module, user_state: user_state} = state) do
job = %{job | by: node()}
:ok = GenServer.call(monitor, {:claim, job})
user_state_args =
case user_state do
{:state, s} -> [s]
:no_state -> []
end
result =
case task do
f when is_function(f) -> apply(f, user_state_args)
f when is_atom(f) -> apply(module, f, user_state_args)
{f, a} -> apply(module, f, a ++ user_state_args)
end
job = %{job | result: {:ok, result}}
with {owner, _ref} <- from,
do: send(owner, job)
:ok = GenServer.call(monitor, :ack)
queue
|> Honeydew.get_queue
|> GenServer.cast({:worker_ready, self()})
{:noreply, state}
end
def handle_cast(:subscribe_to_queues, %State{queue: queue} = state) do
Honeydew.debug "[Honeydew] Worker #{inspect self()} sending ready"
queue
|> Honeydew.get_all_members(:queues)
|> Enum.each(&GenServer.cast(&1, {:worker_ready, self()}))
{:noreply, state}
end
def handle_info({:EXIT, _pid, :normal}, state), do: {:noreply, state}
def handle_info({:EXIT, _pid, :shutdown}, state), do: {:noreply, state}
def handle_info({:EXIT, pid, reason}, state) do
Logger.warn "[Honeydew] Worker #{inspect self()} died because linked process #{inspect pid} crashed"
{:stop, reason, %{state | user_state: nil}}
end
def handle_info({_, {:DOWN, _, :normal}}, state), do: {:noreply, state}
def handle_info({_, {:DOWN, _, :shutdown}}, state), do: {:noreply, state}
def handle_info(msg, state) do
Logger.warn "[Honeydew] Worker #{inspect self()} received unexpected message #{inspect msg}"
{:noreply, state}
end
def terminate(reason, _state) do
Logger.info "[Honeydew] Worker #{inspect self()} stopped because #{inspect reason}"
end
end