Skip to content

Instantly share code, notes, and snippets.

@stevegraham
Last active August 30, 2015 04:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevegraham/2d84a2da5a0c4085d8c6 to your computer and use it in GitHub Desktop.
Save stevegraham/2d84a2da5a0c4085d8c6 to your computer and use it in GitHub Desktop.
Memorex - a cache for expensive code paths
require Memorex
Memorex.start_link([])
# Cache this expensive function call, e.g. external service API
Memorex.get("unique_cache_key", ttl: 30) do
do_some_expensive_shit
end
defmodule Memorex do
use GenServer
@default_options [ttl: 60]
def init(_args) do
{:ok, :ets.new(__MODULE__, [:private])}
end
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
defmacro get(key, opts \\ [], proc) do
quote do
opts = unquote(@default_options) |> Keyword.merge(unquote(opts))
key = :crypto.hash(:sha256, unquote(key))
result = GenServer.call(unquote(__MODULE__), {:get, key})
case result do
nil ->
GenServer.call(unquote(__MODULE__), {:put, key, unquote(proc)[:do], opts[:ttl]})
value ->
value
end
end
end
def handle_call({:get, key}, _from, tid) do
result = case :ets.lookup(tid, key) do
[{^key, value}] -> value
_ -> nil
end
{:reply, result, tid}
end
def handle_call({:put, key, value, ttl}, _from, tid) do
:ets.insert(tid, {key, value})
:erlang.send_after(ttl * 1000, self, {:purge, key})
{:reply, value, tid}
end
def handle_info({:purge, key}, tid) do
:ets.delete(tid, key)
{:noreply, tid}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment