When using external services (usually via an api call) I sometimes want to cache the results for a period of time. Here’s a technique that works for me
In this example I have a cached version of a lookup_function
called lookup_function_cached
that takes a key and returns a result
The module will need to be initialised at startup to create the ets table
defmodule AwesomeApp.Lookup do
def init() do
# I've named the table the same as the module
:ets.new(AwesomeApp.Lookup, [:named_table, :public, read_concurrency: true])
end
def lookup_function(key) do
:timer.sleep(3000)
"3 seconds can feel like a lifetime"
end
end
@ttl 24 * 60 * 60
defp lookup_function_cache_find(key) do
case :ets.lookup(AwesomeApp.Lookup, key) do
[result | _] ->
lookup_function_cache_check(result)
[] ->
nil
end
end
defp lookup_function_cache_check({_, result, expiration}) do
cond do
expiration > :os.system_time(:seconds) ->
result
true ->
nil
end
end
def lookup_function_cached(key) do
case lookup_function_cache_find(hostname) do
{:ok, result} ->
{:ok, result}
_ ->
key
|> lookup_function()
|> lookup_function_cache_save(key)
end
end
defp lookup_function_cache_save(result, key) do
expiration = :os.system_time(:seconds) + @ttl
:ets.insert(AwesomeApp.Lookup, {key, result, expiration})
result
end