make it possible to use multiple LruCache instances
This commit is contained in:
parent
de9c79016a
commit
98bfe3c98b
|
@ -16,24 +16,39 @@ defmodule Floof.Application do
|
||||||
pubkey_mgr = Application.fetch_env!(:floof, :pubkey_mgr)
|
pubkey_mgr = Application.fetch_env!(:floof, :pubkey_mgr)
|
||||||
pubkey_config = Application.fetch_env!(:floof, :pubkey_config)
|
pubkey_config = Application.fetch_env!(:floof, :pubkey_config)
|
||||||
|
|
||||||
children = [
|
children =
|
||||||
|
[
|
||||||
# Starts a worker by calling: Floof.Worker.start_link(arg)
|
# Starts a worker by calling: Floof.Worker.start_link(arg)
|
||||||
# {Floof.Worker, arg}
|
# {Floof.Worker, arg}
|
||||||
{Task.Supervisor, name: Floof.TaskSupervisor},
|
{Task.Supervisor, name: Floof.TaskSupervisor},
|
||||||
{Floof.Distributor, Floof.Distributor.fconfig(
|
{Floof.LruCache,
|
||||||
markers: markers, attrs: attrs, set_markers: set_markers,
|
%{
|
||||||
pubkey_mgr: pubkey_mgr, pubkey_config: pubkey_config
|
:name => Floof.DistributorSeen,
|
||||||
)},
|
:capacity => 4096,
|
||||||
|
:position_table => Floof.DistributorSeen.PositionTable,
|
||||||
|
:cache_table => Floof.DistributorSeen.CacheTable
|
||||||
|
}},
|
||||||
|
{Floof.Distributor,
|
||||||
|
{Floof.Distributor.fconfig(
|
||||||
|
markers: markers,
|
||||||
|
attrs: attrs,
|
||||||
|
set_markers: set_markers,
|
||||||
|
pubkey_mgr: pubkey_mgr,
|
||||||
|
pubkey_config: pubkey_config
|
||||||
|
), Floof.DistributorSeen}},
|
||||||
{Floof.SessionManager, %{}},
|
{Floof.SessionManager, %{}},
|
||||||
{Task, fn -> Floof.accept(port) end},
|
{Task, fn -> Floof.accept(port) end}
|
||||||
] ++ (for upstream <- upstreams do
|
] ++
|
||||||
{host, port} = case upstream do
|
for upstream <- upstreams do
|
||||||
|
{host, port} =
|
||||||
|
case upstream do
|
||||||
{host, port} -> {host, port}
|
{host, port} -> {host, port}
|
||||||
{host} -> {host, 2540}
|
{host} -> {host, 2540}
|
||||||
end
|
end
|
||||||
|
|
||||||
# this establishes connections to upstream hosts
|
# this establishes connections to upstream hosts
|
||||||
{Task, fn -> Floof.connect(host, port) end}
|
{Task, fn -> Floof.connect(host, port) end}
|
||||||
end)
|
end
|
||||||
|
|
||||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
# for other strategies and supported options
|
# for other strategies and supported options
|
||||||
|
|
|
@ -5,13 +5,18 @@ defmodule Floof.LruCache do
|
||||||
|
|
||||||
### Client API
|
### Client API
|
||||||
|
|
||||||
|
def start_link(opts) do
|
||||||
|
opts2 = Map.take(opts, [:capacity, :position_table, :cache_table])
|
||||||
|
GenServer.start_link(__MODULE__, opts2, name: opts.name)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Creates a LRU cache
|
Creates a LRU cache
|
||||||
## Examples
|
## Examples
|
||||||
iex> Floof.LruCache.create()
|
iex> Floof.LruCache.create()
|
||||||
"""
|
"""
|
||||||
def create(capacity \\ 5, name \\ __MODULE__) when is_integer(capacity) do
|
def create(capacity \\ 5, name \\ __MODULE__) when is_integer(capacity) do
|
||||||
GenServer.start_link(__MODULE__, capacity, [name: name])
|
GenServer.start_link(__MODULE__, %{:capacity => capacity}, name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -54,61 +59,72 @@ defmodule Floof.LruCache do
|
||||||
|
|
||||||
### Server Callbacks
|
### Server Callbacks
|
||||||
|
|
||||||
def init(capacity) do
|
def init(opts) do
|
||||||
:ets.new(:position_table, [:named_table, :public]) # kvp = {key: key, value: key_time }
|
opts =
|
||||||
:ets.new(:cache_table, [:named_table, :ordered_set]) # kvp = {key: key_time, time: value}
|
Map.merge(
|
||||||
lru_state = %Floof.LruCache{capacity: capacity}
|
%{:capacity => 5, :position_table => :position_table, :cache_table => :cache_table},
|
||||||
{:ok, lru_state}
|
opts
|
||||||
|
)
|
||||||
|
|
||||||
|
# kvp = {key: key, value: key_time }
|
||||||
|
:ets.new(opts.position_table, [:named_table, :public])
|
||||||
|
# kvp = {key: key_time, time: value}
|
||||||
|
:ets.new(opts.cache_table, [:named_table, :ordered_set])
|
||||||
|
{:ok, opts}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:put, key, value}, _from, lru_state) do
|
def handle_call({:put, key, value}, _from, lru_state) do
|
||||||
wasUpdated = insert_kvp(key, value)
|
wasUpdated = insert_kvp(lru_state, key, value)
|
||||||
remove_least_recently_used(lru_state)
|
remove_least_recently_used(lru_state)
|
||||||
{:reply, wasUpdated, lru_state}
|
{:reply, wasUpdated, lru_state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:get, key}, _from, lru_state) do
|
def handle_call({:get, key}, _from, lru_state) do
|
||||||
time_result = :ets.lookup(:position_table, key)
|
time_result = :ets.lookup(lru_state.position_table, key)
|
||||||
|
|
||||||
case time_result do
|
case time_result do
|
||||||
[{_, time_key}] ->
|
[{_, time_key}] ->
|
||||||
val = update_item_position(key, time_key)
|
val = update_item_position(lru_state, key, time_key)
|
||||||
{:reply, val, lru_state}
|
{:reply, val, lru_state}
|
||||||
[] -> # key not found in cache
|
|
||||||
|
# key not found in cache
|
||||||
|
[] ->
|
||||||
{:reply, nil, lru_state}
|
{:reply, nil, lru_state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:delete, key}, _from, lru_state) do
|
def handle_call({:delete, key}, _from, lru_state) do
|
||||||
result = :ets.delete(:position_table, key)
|
result = :ets.delete(lru_state.position_table, key)
|
||||||
:ets.delete(:cache_table, key)
|
:ets.delete(lru_state.cache_table, key)
|
||||||
{:reply, result, lru_state}
|
{:reply, result, lru_state}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp remove_least_recently_used(lru_state) do
|
defp remove_least_recently_used(lru_state) do
|
||||||
# if we exceed capacity remove least recently used item
|
# if we exceed capacity remove least recently used item
|
||||||
num_items = :ets.info(:position_table, :size)
|
num_items = :ets.info(lru_state.position_table, :size)
|
||||||
|
|
||||||
if num_items > lru_state.capacity do
|
if num_items > lru_state.capacity do
|
||||||
time_key = :ets.first(:cache_table)
|
time_key = :ets.first(lru_state.cache_table)
|
||||||
[{_, {key, _val}}] = :ets.lookup(:cache_table, time_key)
|
[{_, {key, _val}}] = :ets.lookup(lru_state.cache_table, time_key)
|
||||||
:ets.delete(:cache_table, time_key)
|
:ets.delete(lru_state.cache_table, time_key)
|
||||||
:ets.delete(:position_table, key)
|
:ets.delete(lru_state.position_table, key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_item_position(key, time_key) do
|
defp update_item_position(lru_state, key, time_key) do
|
||||||
# Puts item in back of table
|
# Puts item in back of table
|
||||||
counter = :erlang.unique_integer([:monotonic])
|
counter = :erlang.unique_integer([:monotonic])
|
||||||
[{_, {_key, val}}] = :ets.lookup(:cache_table, time_key)
|
[{_, {_key, val}}] = :ets.lookup(lru_state.cache_table, time_key)
|
||||||
:ets.delete(:cache_table, time_key)
|
:ets.delete(lru_state.cache_table, time_key)
|
||||||
:ets.insert(:cache_table, {counter, {key, val}})
|
:ets.insert(lru_state.cache_table, {counter, {key, val}})
|
||||||
:ets.insert(:position_table, {key, counter})
|
:ets.insert(lru_state.position_table, {key, counter})
|
||||||
val
|
val
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_kvp(key, value) do
|
defp insert_kvp(lru_state, key, value) do
|
||||||
counter = :erlang.unique_integer([:monotonic])
|
counter = :erlang.unique_integer([:monotonic])
|
||||||
wasUpdated = :ets.insert(:position_table, {key, counter})
|
wasUpdated = :ets.insert(lru_state.position_table, {key, counter})
|
||||||
:ets.insert(:cache_table, {counter, {key, value}})
|
:ets.insert(lru_state.cache_table, {counter, {key, value}})
|
||||||
wasUpdated
|
wasUpdated
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,8 @@ defmodule LruCacheTest do
|
||||||
doctest Floof.LruCache
|
doctest Floof.LruCache
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
{:ok,server_pid} = Floof.LruCache.create(3, :LruCache)
|
{:ok, server_pid} = Floof.LruCache.create(3, :LruCache)
|
||||||
{:ok,server: server_pid}
|
{:ok, server: server_pid}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tests put() returns true when adding element" do
|
test "tests put() returns true when adding element" do
|
||||||
|
|
Loading…
Reference in a new issue