Skip to content

Commit

Permalink
feat: add jitter option to throttle function (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
btkostner authored Dec 13, 2023
1 parent 68bdd18 commit 7991f91
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 2 deletions.
16 changes: 15 additions & 1 deletion lib/buffy/throttle.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ defmodule Buffy.Throttle do
## Options
- `:jitter` (`integer`) - Optional. The amount of jitter or randomosity to add to the throttle function handle. This value is in milliseconds. Defaults to `0`.
- `:registry_module` (`atom`) - Optional. A module that implements the `Registry` behaviour. If you are running in a distributed instance, you can set this value to `Horde.Registry`. Defaults to `Registry`.
- `:registry_name` (`atom`) - Optional. The name of the registry to use. Defaults to the built in Buffy registry, but if you are running in a distributed instance you can set this value to a named `Horde.Registry` process. Defaults to `Buffy.Registry`.
Expand All @@ -49,7 +51,7 @@ defmodule Buffy.Throttle do
- `:supervisor_name` (`atom`) - Optional. The name of the dynamic supervisor to use. Defaults to the built in Buffy dynamic supervisor, but if you are running in a distributed instance you can set this value to a named `Horde.DynamicSupervisor` process. Defaults to `Buffy.DynamicSupervisor`.
- :throttle (`non_neg_integer`) - Required. The amount of time to wait before invoking the function. This value is in milliseconds.
- :throttle (`non_neg_integer`) - Required. The minimum amount of time to wait before invoking the function. This value is in milliseconds. The actual run time could be longer than this value based on the `:jitter` option.
## Using with Horde
Expand All @@ -73,6 +75,7 @@ defmodule Buffy.Throttle do
These are the events that are called by the `Buffy.Throttle` module:
- `[:buffy, :throttle, :throttle]` - Emitted when the `throttle/1` function is called.
- `[:buffy, :throttle, :handle, :jitter]` - Emitted before the `handle_throttle/1` function is called with the amount of jitter added to the throttle.
- `[:buffy, :throttle, :handle, :start]` - Emitted at the start of the `handle_throttle/1` function.
- `[:buffy, :throttle, :handle, :stop]` - Emitted at the end of the `handle_throttle/1` function.
- `[:buffy, :throttle, :handle, :exception]` - Emitted when an error is raised in the `handle_throttle/1` function.
Expand Down Expand Up @@ -120,6 +123,7 @@ defmodule Buffy.Throttle do
@callback handle_throttle(args()) :: any()

defmacro __using__(opts) do
jitter = Keyword.get(opts, :jitter, 0)
registry_module = Keyword.get(opts, :registry_module, Registry)
registry_name = Keyword.get(opts, :registry_name, Buffy.Registry)
restart = Keyword.get(opts, :restart, :temporary)
Expand Down Expand Up @@ -219,6 +223,16 @@ defmodule Buffy.Throttle do
@impl GenServer
@spec handle_info(:timeout, Buffy.Throttle.state()) :: {:stop, :normal, Buffy.Throttle.state()}
def handle_info(:timeout, {key, args}) do
selected_jitter = max(:rand.uniform(unquote(jitter) + 1) - 1, 0)

:telemetry.execute([:buffy, :throttle, :handle, :jitter], %{jitter: selected_jitter}, %{
args: args,
key: key,
module: __MODULE__
})

Process.sleep(selected_jitter)

:telemetry.span(
[:buffy, :throttle, :handle],
%{args: args, key: key, module: __MODULE__},
Expand Down
16 changes: 15 additions & 1 deletion test/buffy/throttle_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ defmodule Buffy.ThrottleTest do
use ExUnitProperties

setup do
spy(MyZeroThrottler)
spy(MySlowThrottler)
spy(MyZeroThrottler)
:ok
end

Expand All @@ -32,6 +32,7 @@ defmodule Buffy.ThrottleTest do
_ref =
:telemetry_test.attach_event_handlers(self(), [
[:buffy, :throttle, :throttle],
[:buffy, :throttle, :handle, :jitter],
[:buffy, :throttle, :handle, :start],
[:buffy, :throttle, :handle, :stop],
[:buffy, :throttle, :handle, :exception]
Expand All @@ -51,6 +52,19 @@ defmodule Buffy.ThrottleTest do
}}
end

test "emits [:buffy, :throttle, :handle, :jitter]" do
MyJitterThrottler.throttle(:foo)

assert_receive {[:buffy, :throttle, :handle, :jitter], _ref, measurements,
%{
args: :foo,
key: _,
module: MyJitterThrottler
}}

assert measurements.jitter >= 0
end

test "emits [:buffy, :throttle, :handle, :start]" do
MyZeroThrottler.throttle(:starting)

Expand Down
9 changes: 9 additions & 0 deletions test/support/my_jitter_throttler.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule MyJitterThrottler do
use Buffy.Throttle,
jitter: 100,
throttle: 0

def handle_throttle(_args) do
:ok
end
end

0 comments on commit 7991f91

Please sign in to comment.