Packages

Rate limiting tool.

Current section

Files

Jump to
ral lib ral_ets.ex
Raw

lib/ral_ets.ex

defmodule Ral.ETS do
use GenServer
alias :ets, as: ETS
@score :ral_score
@member :ral_member
def start_link(_) do
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
end
def init(_) do
ETS.new(@member, [
:set,
:protected,
:named_table,
{:write_concurrency, true},
{:read_concurrency, true}
])
ETS.new(@score, [
:ordered_set,
:protected,
:named_table,
{:write_concurrency, true},
{:read_concurrency, true}
])
{:ok, nil}
end
def handle_cast({:insert, key, now, rest}, state) do
ETS.insert(@member, {key, now, rest})
ETS.insert(@score, {{now, key}})
{:noreply, state}
end
def handle_cast({:delete, d_score?, {prev, key}}, state) do
if d_score?, do: ETS.delete(@score, {prev, key})
delete_if_expired?(ETS.first(@score), key)
{:noreply, state}
end
def insert({key, now, rest}) do
GenServer.cast(__MODULE__, {:insert, key, now, rest})
end
def delete({d_score?, {prev, key}}) do
GenServer.cast(__MODULE__, {:delete, d_score?, {prev, key}})
end
@spec delete_if_expired?(:"$end_of_table" | {DateTime.t(), Atom}, Atom) :: nil
defp delete_if_expired?(:"$end_of_table", _), do: nil
defp delete_if_expired?({prev, key}, new_key) when key != new_key do
if DateTime.diff(DateTime.utc_now(), prev) >= 300 do
ETS.delete(@member, key)
ETS.delete(@score, {prev, key})
delete_if_expired?(ETS.first(@score), new_key)
end
end
defp delete_if_expired?(_, _), do: nil
end