Packages
quantum
2.0.3
3.5.3
3.5.2
retired
3.5.1
retired
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
retired
2.3.0
retired
2.2.7
2.2.6
2.2.5
retired
2.2.4
retired
2.2.3
retired
2.2.2
retired
2.2.1
retired
2.2.0
retired
2.1.3
retired
2.1.2
retired
2.1.1
retired
2.1.0
retired
2.1.0-beta.1
retired
2.0.4
retired
2.0.3
retired
2.0.2
retired
2.0.1
retired
2.0.0
retired
2.0.0-beta.2
retired
2.0.0-beta.1
retired
1.9.3
retired
1.9.2
retired
1.9.1
retired
1.9.0
retired
1.8.1
retired
1.8.0
retired
1.7.1
retired
1.7.0
retired
1.6.1
retired
1.6.0
retired
1.5.0
retired
1.4.0
retired
1.3.2
retired
1.3.1
retired
1.3.0
retired
1.2.4
retired
1.2.3
retired
1.2.2
retired
1.2.1
retired
1.2.0
retired
1.1.0
retired
1.0.4
retired
1.0.3
retired
1.0.2
retired
1.0.1
retired
1.0.0
retired
Cron-like job scheduler for Elixir.
Retired package: Release invalid - Executor Broken, Please upgrade to a newer version
Current section
Files
Jump to
Current section
Files
lib/quantum/runner.ex
defmodule Quantum.Runner do
@moduledoc false
use GenServer
alias Quantum.Job
alias Quantum.Timer
alias Quantum.RunStrategy.NodeList
require Logger
@doc """
Starts Quantum process
"""
def start_link(opts) do
state = %{opts: opts, jobs: Keyword.fetch!(opts, :jobs)}
case GenServer.start_link(__MODULE__, state, [name: Keyword.fetch!(opts, :runner)]) do
{:ok, pid} ->
{:ok, pid}
{:error, {:already_started, pid}} ->
Process.link(pid)
{:ok, pid}
end
end
def init(state) do
new_state = state
|> Map.put(:reboot, true)
|> Map.put(:jobs, run(state))
|> Map.put(:date, Timer.tick())
|> Map.put(:reboot, false)
{:ok, new_state}
end
def handle_call({:add, job}, _, %{jobs: jobs} = state) do
{:reply, :ok, %{state | jobs: [job | jobs]}}
end
def handle_call({:change_state, name, job_state}, _, %{jobs: jobs} = state) do
if Keyword.has_key?(jobs, name) do
job = jobs
|> Keyword.fetch!(name)
|> Job.set_state(job_state)
new_jobs = Keyword.put(jobs, name, job)
{:reply, :ok, %{state | jobs: new_jobs}}
else
{:reply, {:error, :not_found}, state}
end
end
def handle_call({:delete, name}, _, %{jobs: jobs} = state) do
if Keyword.has_key?(jobs, name) do
{:reply, :ok, %{state | jobs: List.keydelete(jobs, name, 0)}}
else
{:reply, {:error, :not_found}, state}
end
end
def handle_call({:delete_all}, _, state) do
{:reply, :ok, %{state | jobs: []}}
end
def handle_call(:jobs, _, %{jobs: jobs} = state), do: {:reply, jobs, state}
def handle_call({:find_job, name}, _, %{jobs: jobs} = state) do
{:reply, Keyword.get(jobs, name), state}
end
def handle_info(:tick, state) do
new_state = Map.put(state, :date, Timer.tick())
{:noreply, %{new_state | jobs: run(new_state)}}
end
def handle_info(_, state), do: {:noreply, state}
defp run(state) do
task_supervisor = Keyword.fetch!(state.opts, :task_supervisor)
Enum.map state.jobs, fn({name, job}) ->
pids = job.run_strategy
|> NodeList.nodes(job)
|> Enum.filter(&running_node?(&1, task_supervisor))
|> Enum.map(&run_if_possible(&1, job, state, task_supervisor))
|> Enum.reject(fn ({_, pid}) -> pid == nil end)
|> Enum.reduce(job.pids, fn({node, pid}, acc) ->
Keyword.put(acc, node, pid)
end)
{name, %{job | pids: pids}}
end
end
defp running_node?(node, _) when node == node(), do: true
defp running_node?(node, task_supervisor) do
node
|> :rpc.call(:erlang, :whereis, [task_supervisor])
|> is_pid()
end
defp run_if_possible(node, job, state, task_supervisor) do
cond do
!Job.executable?(job, node) ->
{node, Keyword.get(job.pids, node, nil)}
!Enum.member?([node() | Node.list()], node) ->
Logger.warn("Node #{inspect node} is not in cluster. Skipping.")
{node, Keyword.get(job.pids, node, nil)}
true ->
task = Task.Supervisor.async_nolink({task_supervisor, node}, Quantum.Executor,
:execute, [{job.schedule, job.task, job.timezone}, state])
{node, task.pid}
end
end
end