Current section
Files
Jump to
Current section
Files
lib/rate_limiter_man.ex
defmodule RateLimiterMan do
@moduledoc """
A rate limiter implementation, based heavily on a blog post by [Alex
Koutmous](https://akoutmos.com/post/rate-limiting-with-genservers/).
This package handles logic for limiting the rate at which HTTP requests are sent.
"""
@callback make_request(atom(), atom(), tuple(), keyword()) :: :ok
def make_request(otp_app, config_key, request_handler, opts \\ []) do
rate_limiter = get_rate_limiter(otp_app, config_key)
rate_limiter.make_request(otp_app, config_key, request_handler, opts)
end
def calculate_refresh_rate(otp_app, config_key) do
max_requests_per_second = get_max_requests_per_second(otp_app, config_key)
floor(:timer.seconds(1) / max_requests_per_second)
end
def get_rate_limiter(otp_app, config_key),
do: get_config(otp_app, config_key, :rate_limiter_algorithm)
def get_max_requests_per_second(otp_app, config_key),
do: fetch_config!(otp_app, config_key, :rate_limiter_max_requests_per_second)
@doc "Receive a response from a rate limiter."
def receive_response(unique_request_id, timeout \\ :timer.seconds(15)) do
receive do
{:ok, %{request_id: request_id, response: response}} when request_id == unique_request_id ->
response
after
timeout -> {:error, :timeout}
end
end
@doc "Get the process name for a rate limiter instance by its `config_key`."
def get_instance_name(config_key),
do: String.to_atom("#{Macro.underscore(config_key)}_leaky_bucket_rate_limiter")
def start_rate_limiter(otp_app, config_key),
do: {get_rate_limiter(otp_app, config_key), %{otp_app: otp_app, config_key: config_key}}
def start_task_supervisor, do: {Task.Supervisor, name: RateLimiterMan.TaskSupervisor}
def get_config(otp_app, config_key, subkey, default \\ nil),
do: Application.fetch_env!(otp_app, config_key) |> Keyword.get(subkey, default)
def fetch_config!(otp_app, config_key, subkey),
do: Application.fetch_env!(otp_app, config_key) |> Keyword.fetch!(subkey)
end