this repo has no description

Simple cache implementation

We could use `Nebulex` or `Cachex` there, but we do not need features
these tools bring us (and with that - their complexities). Simple ETS
table is more than enough to fulfill our needs.

hauleth.dev 85b2521d 12ee1da6

verified
Changed files
+93 -2
lib
esl_hn
test
+1 -1
.formatter.exs
···
[
line_length: 80,
-
import_deps: [:phoenix, :ecto],
+
import_deps: [:phoenix, :stream_data, :ecto],
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
]
+52
lib/esl_hn/cache.ex
···
+
defmodule EslHn.Cache do
+
use GenServer
+
+
def start_link(opts) do
+
with {:ok, pid} <- GenServer.start_link(__MODULE__, opts) do
+
tid = GenServer.call(pid, :get_tid)
+
+
{:ok, pid, tid}
+
end
+
end
+
+
def read(tid, key) do
+
case :ets.lookup(tid, key) do
+
[{^key, data}] -> {:ok, data}
+
_ -> :error
+
end
+
end
+
+
def write(tid, key, data) do
+
true = :ets.insert(tid, {key, data})
+
+
:ok
+
end
+
+
@impl true
+
def init(opts) do
+
{name, opts} =
+
case Access.fetch(opts, :name) do
+
{:ok, name} -> {name, [:named_table]}
+
:error -> {nil, []}
+
end
+
+
tid =
+
:ets.new(
+
name,
+
opts ++
+
[
+
:set,
+
:public,
+
write_concurrency: :auto,
+
read_concurrency: true
+
]
+
)
+
+
{:ok, %{tid: tid}}
+
end
+
+
@impl true
+
def handle_call(:get_tid, _ref, state) do
+
{:reply, state.tid, state}
+
end
+
end
+2 -1
mix.exs
···
{:credo, ">= 0.0.0", only: [:dev]},
# Testing
-
{:test_server, "~> 0.1.21", only: [:test]}
+
{:test_server, "~> 0.1.21", only: [:test]},
+
{:stream_data, "~> 1.0", only: [:dev, :test]}
]
end
+1
mix.lock
···
"plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"req": {:hex, :req, "0.5.15", "662020efb6ea60b9f0e0fac9be88cd7558b53fe51155a2d9899de594f9906ba9", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a6513a35fad65467893ced9785457e91693352c70b58bbc045b47e5eb2ef0c53"},
+
"stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
+37
test/esl_hn/cache_test.exs
···
+
defmodule EslHn.CacheTest do
+
use ExUnit.Case, async: true
+
use ExUnitProperties
+
+
@subject EslHn.Cache
+
+
doctest @subject
+
+
describe "unnamed cache" do
+
property "we can read written data" do
+
assert {:ok, _pid, tid} = start_supervised(@subject)
+
+
check all(key <- term(), data <- term()) do
+
assert :ok == @subject.write(tid, key, data)
+
assert {:ok, data} == @subject.read(tid, key)
+
end
+
end
+
+
test "fetching non existent key result in error" do
+
assert {:ok, _pid, tid} = start_supervised(@subject)
+
+
assert :error == @subject.read(tid, :non_existent)
+
end
+
end
+
+
describe "named cache" do
+
property "we can read written data", ctx do
+
name = ctx.test
+
assert {:ok, _pid, _tid} = start_supervised({@subject, name: name})
+
+
check all(key <- term(), data <- term()) do
+
assert :ok == @subject.write(name, key, data)
+
assert {:ok, data} == @subject.read(name, key)
+
end
+
end
+
end
+
end