-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/backoff'
Fixes: #23
- Loading branch information
Showing
7 changed files
with
224 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
defmodule Kitto.Backoff do | ||
@moduledoc """ | ||
Specification for a backoff module to be used with Kitto. | ||
""" | ||
|
||
@callback succeed(atom) :: any | ||
@callback fail(atom):: any | ||
@callback backoff!(atom) :: any | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
defmodule Kitto.BackoffServerTest do | ||
use ExUnit.Case, async: true | ||
|
||
alias Kitto.BackoffServer, as: Subject | ||
|
||
@min 1 | ||
|
||
setup do | ||
Subject.reset | ||
|
||
on_exit fn -> | ||
Application.delete_env :kitto, :job_min_backoff | ||
Application.delete_env :kitto, :job_max_backoff | ||
end | ||
end | ||
|
||
test "#succeed resets to 0 the backoff for a job" do | ||
Subject.succeed :italian_job | ||
|
||
assert Subject.get(:italian_job) == 0 | ||
end | ||
|
||
test "#reset resets the state of the server to an empty map" do | ||
Subject.fail :failjob | ||
Subject.fail :otherjob | ||
Subject.succeed :successjob | ||
|
||
Subject.reset | ||
|
||
assert is_nil(Subject.get(:failjob)) | ||
assert is_nil(Subject.get(:otherjob)) | ||
assert is_nil(Subject.get(:successjob)) | ||
end | ||
|
||
test "#fail increases the backoff value exponentially (power of 2)" do | ||
Subject.fail :failjob | ||
|
||
val = Subject.get :failjob | ||
|
||
Subject.fail :failjob | ||
assert Subject.get(:failjob) == val * 2 | ||
|
||
Subject.fail :failjob | ||
assert Subject.get(:failjob) == val * 4 | ||
end | ||
|
||
test "#backoff! puts the current process to sleep for backoff time" do | ||
maxval = 100 | ||
Application.put_env :kitto, :job_mix_backoff, 64 | ||
Application.put_env :kitto, :job_max_backoff, maxval | ||
Subject.fail :failjob | ||
|
||
{time, _} = :timer.tc fn -> Subject.backoff! :failjob end | ||
|
||
assert_in_delta time / 1000, maxval, 5 | ||
end | ||
|
||
describe "when :job_min_backoff is configured" do | ||
setup [:set_job_min_backoff] | ||
|
||
test "#fail initializes the backoff to the min value" do | ||
Subject.fail :failjob | ||
|
||
assert Subject.get(:failjob) == @min | ||
end | ||
end | ||
|
||
describe "when :job_min_backoff is not configured" do | ||
test "#fail initializes the backoff to the default min value" do | ||
Subject.fail :failjob | ||
|
||
assert Subject.get(:failjob) == Kitto.Time.mseconds(:second) | ||
end | ||
end | ||
|
||
defp set_job_min_backoff(_context) do | ||
Application.put_env :kitto, :job_min_backoff, @min | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters