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
··· 1 1 [ 2 2 line_length: 80, 3 - import_deps: [:phoenix, :ecto], 3 + import_deps: [:phoenix, :stream_data, :ecto], 4 4 inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"] 5 5 ]
+52
lib/esl_hn/cache.ex
··· 1 + defmodule EslHn.Cache do 2 + use GenServer 3 + 4 + def start_link(opts) do 5 + with {:ok, pid} <- GenServer.start_link(__MODULE__, opts) do 6 + tid = GenServer.call(pid, :get_tid) 7 + 8 + {:ok, pid, tid} 9 + end 10 + end 11 + 12 + def read(tid, key) do 13 + case :ets.lookup(tid, key) do 14 + [{^key, data}] -> {:ok, data} 15 + _ -> :error 16 + end 17 + end 18 + 19 + def write(tid, key, data) do 20 + true = :ets.insert(tid, {key, data}) 21 + 22 + :ok 23 + end 24 + 25 + @impl true 26 + def init(opts) do 27 + {name, opts} = 28 + case Access.fetch(opts, :name) do 29 + {:ok, name} -> {name, [:named_table]} 30 + :error -> {nil, []} 31 + end 32 + 33 + tid = 34 + :ets.new( 35 + name, 36 + opts ++ 37 + [ 38 + :set, 39 + :public, 40 + write_concurrency: :auto, 41 + read_concurrency: true 42 + ] 43 + ) 44 + 45 + {:ok, %{tid: tid}} 46 + end 47 + 48 + @impl true 49 + def handle_call(:get_tid, _ref, state) do 50 + {:reply, state.tid, state} 51 + end 52 + end
+2 -1
mix.exs
··· 41 41 {:credo, ">= 0.0.0", only: [:dev]}, 42 42 43 43 # Testing 44 - {:test_server, "~> 0.1.21", only: [:test]} 44 + {:test_server, "~> 0.1.21", only: [:test]}, 45 + {:stream_data, "~> 1.0", only: [:dev, :test]} 45 46 ] 46 47 end 47 48
+1
mix.lock
··· 22 22 "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"}, 23 23 "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, 24 24 "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"}, 25 + "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, 25 26 "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, 26 27 "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, 27 28 "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
··· 1 + defmodule EslHn.CacheTest do 2 + use ExUnit.Case, async: true 3 + use ExUnitProperties 4 + 5 + @subject EslHn.Cache 6 + 7 + doctest @subject 8 + 9 + describe "unnamed cache" do 10 + property "we can read written data" do 11 + assert {:ok, _pid, tid} = start_supervised(@subject) 12 + 13 + check all(key <- term(), data <- term()) do 14 + assert :ok == @subject.write(tid, key, data) 15 + assert {:ok, data} == @subject.read(tid, key) 16 + end 17 + end 18 + 19 + test "fetching non existent key result in error" do 20 + assert {:ok, _pid, tid} = start_supervised(@subject) 21 + 22 + assert :error == @subject.read(tid, :non_existent) 23 + end 24 + end 25 + 26 + describe "named cache" do 27 + property "we can read written data", ctx do 28 + name = ctx.test 29 + assert {:ok, _pid, _tid} = start_supervised({@subject, name: name}) 30 + 31 + check all(key <- term(), data <- term()) do 32 + assert :ok == @subject.write(name, key, data) 33 + assert {:ok, data} == @subject.read(name, key) 34 + end 35 + end 36 + end 37 + end