zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

backoff.ex (2466B)


      1 defmodule DBConnection.Backoff do
      2   @moduledoc false
      3   @compile :nowarn_deprecated_function
      4 
      5   alias DBConnection.Backoff
      6 
      7   @default_type :rand_exp
      8   @min 1_000
      9   @max 30_000
     10 
     11   defstruct [:type, :min, :max, :state]
     12 
     13   def new(opts) do
     14     case Keyword.get(opts, :backoff_type, @default_type) do
     15       :stop ->
     16         nil
     17 
     18       type ->
     19         {min, max} = min_max(opts)
     20         new(type, min, max)
     21     end
     22   end
     23 
     24   def backoff(%Backoff{type: :rand, min: min, max: max} = s) do
     25     {rand(min, max), s}
     26   end
     27 
     28   def backoff(%Backoff{type: :exp, min: min, state: nil} = s) do
     29     {min, %Backoff{s | state: min}}
     30   end
     31 
     32   def backoff(%Backoff{type: :exp, max: max, state: prev} = s) do
     33     require Bitwise
     34     next = min(Bitwise.<<<(prev, 1), max)
     35     {next, %Backoff{s | state: next}}
     36   end
     37 
     38   def backoff(%Backoff{type: :rand_exp, max: max, state: state} = s) do
     39     {prev, lower} = state
     40     next_min = min(prev, lower)
     41     next_max = min(prev * 3, max)
     42     next = rand(next_min, next_max)
     43     {next, %Backoff{s | state: {next, lower}}}
     44   end
     45 
     46   def reset(%Backoff{type: :rand} = s), do: s
     47   def reset(%Backoff{type: :exp} = s), do: %Backoff{s | state: nil}
     48 
     49   def reset(%Backoff{type: :rand_exp, min: min, state: {_, lower}} = s) do
     50     %Backoff{s | state: {min, lower}}
     51   end
     52 
     53   ## Internal
     54 
     55   defp min_max(opts) do
     56     case {opts[:backoff_min], opts[:backoff_max]} do
     57       {nil, nil} -> {@min, @max}
     58       {nil, max} -> {min(@min, max), max}
     59       {min, nil} -> {min, max(min, @max)}
     60       {min, max} -> {min, max}
     61     end
     62   end
     63 
     64   defp new(_, min, _) when not (is_integer(min) and min >= 0) do
     65     raise ArgumentError, "minimum #{inspect(min)} not 0 or a positive integer"
     66   end
     67 
     68   defp new(_, _, max) when not (is_integer(max) and max >= 0) do
     69     raise ArgumentError, "maximum #{inspect(max)} not 0 or a positive integer"
     70   end
     71 
     72   defp new(_, min, max) when min > max do
     73     raise ArgumentError, "minimum #{min} is greater than maximum #{max}"
     74   end
     75 
     76   defp new(:rand, min, max) do
     77     %Backoff{type: :rand, min: min, max: max, state: nil}
     78   end
     79 
     80   defp new(:exp, min, max) do
     81     %Backoff{type: :exp, min: min, max: max, state: nil}
     82   end
     83 
     84   defp new(:rand_exp, min, max) do
     85     lower = max(min, div(max, 3))
     86     %Backoff{type: :rand_exp, min: min, max: max, state: {min, lower}}
     87   end
     88 
     89   defp new(type, _, _) do
     90     raise ArgumentError, "unknown type #{inspect(type)}"
     91   end
     92 
     93   defp rand(min, max) do
     94     :rand.uniform(max - min + 1) + min - 1
     95   end
     96 end