Current section
Files
Jump to
Current section
Files
lib/kitto/backoff_server.ex
defmodule Kitto.BackoffServer do
@moduledoc """
Module responsible for keeping and applying a backoff value
for a given atom.
### Configuration
* `:job_min_backoff` - The minimum time in milliseconds to backoff upon failure
* `:job_max_backoff` - The maximum time in milliseconds to backoff upon failure
"""
@behaviour Kitto.Backoff
use GenServer
use Bitwise
alias Kitto.Time
@server __MODULE__
@minval Time.mseconds(:second)
@maxval Time.mseconds({5, :minutes})
@doc false
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: opts[:name] || __MODULE__)
end
@doc false
def init(_), do: {:ok, %{}}
@doc """
Resets the backoff for the given atom to 0
"""
def succeed(name), do: set(name, 0)
@doc """
Increments the backoff value for the provided atom up to the
configured maximum value.
"""
def fail(name) do
case get(name) do
nil -> set(name, min(minval, maxval))
0 -> set(name, min(minval, maxval))
val -> set(name, min(val <<< 1, maxval))
end
end
@doc """
Makes the calling process sleep for the accumulated backoff time
for the given atom
"""
def backoff!(name), do: backoff!(name, name |> get)
defp backoff!(_name, val) when is_nil(val) or val == 0, do: :nop
defp backoff!(_name, val), do: :timer.sleep(val)
def get(name), do: GenServer.call(@server, {:get, name})
def reset, do: GenServer.call(@server, :reset)
### Callbacks
def handle_call(:reset, _from, _state), do: {:reply, nil, %{}}
def handle_call({:get, name}, _from, state), do: {:reply, state[name], state}
def handle_call({:set, name, value}, _from, state) do
{:reply, name, put_in(state[name], value)}
end
defp set(name, value), do: GenServer.call(@server, {:set, name, value})
defp minval, do: Application.get_env(:kitto, :job_min_backoff, @minval)
defp maxval, do: Application.get_env(:kitto, :job_max_backoff, @maxval)
end