Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import Config
config :ex_banking,
max_requests: 20,
rate_units: :seconds,
sweep_rate: 60
sweep_rate: 60,
ets_table: :rater_limiter_requests
45 changes: 29 additions & 16 deletions lib/ex_banking/rate_limiter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,44 @@ defmodule ExBanking.RateLimiter do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def log_request(uid) do
GenServer.call(__MODULE__, {:log_request, uid})
@spec log_request( user :: String.t()) :: :ok | {:error, :too_many_requests_to_user}
def log_request(user) do
ets_table = get_ets_table()
max_requests = Application.get_env(:ex_banking, :max_requests)

case :ets.update_counter(ets_table, user, {2, 1}, {user, 0}) do
count when count > max_requests -> {:error, :too_many_requests_to_user}
_count -> :ok
end
end

@impl true
@spec init(any) :: {:ok, %{requests: %{}}}
@spec init(any) :: {:ok, %{}}
def init(_) do
ets_table = get_ets_table()

:ets.new(ets_table, [
:set,
:named_table,
:public,
read_concurrency: true,
write_concurrency: true
])

schedule_sweep()
{:ok, %{requests: %{}}}
{:ok, %{}}
end

@impl true
def handle_info(:sweep, state) do
Logger.debug("Sweeping requests")
schedule_sweep()
{:noreply, %{state | requests: %{}}}
end

@impl true
def handle_call({:log_request, uid}, _from, state) do
max_requests = Application.get_env(:ex_banking, :max_requests)
ets_table = get_ets_table()
:ets.delete_all_objects(ets_table)

case state.requests[uid] do
count when is_nil(count) or count < max_requests ->
{:reply, :ok, put_in(state, [:requests, uid], (count || 0) + 1)}
schedule_sweep()

count when count >= max_requests ->
{:reply, {:error, :too_many_requests_to_user}, state}
end
{:noreply, state}
end

defp schedule_sweep do
Expand All @@ -47,4 +56,8 @@ defmodule ExBanking.RateLimiter do

Process.send_after(self(), :sweep, sweep_after)
end

defp get_ets_table do
Application.get_env(:ex_banking, :ets_table)
end
end