Skip to content

Instantly share code, notes, and snippets.

@christhekeele
Last active December 13, 2017 19:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christhekeele/1c0dc69d23931c69e1d50fc2fe9ca842 to your computer and use it in GitHub Desktop.
Save christhekeele/1c0dc69d23931c69e1d50fc2fe9ca842 to your computer and use it in GitHub Desktop.
An example meta store for Mnemonix
defmodule Mnemonix.Stores.Meta.PassThrough do
@moduledoc """
A `Mnemonix.Store` that caches reads from a backend store into a frontend one.
Writes and removals are applied to both stores.
Works best with quicker or closer stores in the frontend, like in-memory ones;
with a store-wide ttl to keep their footprint light.
iex> {:ok, backend} = Mnemonix.Stores.Redix.start_link()
iex> {:ok, frontend} = Mnemonix.Stores.ETS.start_link()
iex> {:ok, passthrough} = Mnemonix.Stores.Meta.PassThrough.start_link(frontend: frontend, backend: backend)
iex> Mnemonix.put(backend, "foo", "bar")
iex> Mnemonix.get(frontend, "foo")
nil
iex> Mnemonix.get(passthrough, "foo")
"bar"
iex> Mnemonix.get(frontend, "foo")
"bar"
iex> Mnemonix.put(passthrough, "foo", "baz")
iex> Mnemonix.get(frontend, "foo")
"baz"
iex> Mnemonix.get(backend, "foo")
"baz"
iex> Mnemonix.delete(passthrough, "foo")
iex> Mnemonix.get(frontend, "foo")
nil
iex> Mnemonix.get(backend, "foo")
nil
iex> {nil, ^passthrough} = Mnemonix.get_and_update(passthrough, "foo", &({&1, {&1, &1}}))
iex> Mnemonix.get(passthrough, "foo")
{nil, nil}
iex> Mnemonix.Stores.Meta.PassThrough.__info__(:functions) |> IO.inspect(limit: :infinity)
#=> [bump: 3, bump!: 3, child_spec: 0, child_spec: 1, collectable_into: 2,
#=> decrement: 2, decrement: 3, delete: 2, deserialize_key: 2,
#=> deserialize_value: 2, do_bump: 4, do_bump_calculation: 3, drop: 2,
#=> enumerable?: 1, enumerable_count: 1, enumerable_member?: 2,
#=> enumerable_reduce: 3, fetch: 2, fetch!: 2, get: 2, get: 3, get_and_update: 3,
#=> get_and_update!: 3, get_lazy: 3, has_key?: 2, increment: 2, increment: 3,
#=> keys: 1, msg_for: 2, pop: 2, pop: 3, pop_lazy: 3, put: 3, put_new: 3,
#=> put_new_lazy: 3, replace: 3, replace!: 3, serialize_key: 2, serialize_value: 2,
#=> setup: 1, setup_initial: 1, split: 2, start_link: 0, start_link: 1, take: 2,
#=> teardown: 2, to_enumerable: 1, to_list: 1, update: 4, update!: 3, values: 1]
This store raises errors on the functions in `Mnemonix.Features.Enumerable`.
"""
alias Mnemonix.Store
use Store.Behaviour
use Store.Translator.Raw
####
# Mnemonix.Store.Behaviours.Core
##
@doc """
Tracks frontend and backend stores furnished in `opts`.
## Options
- `frontend:` A store reference to cache reads from the backend store in.
- `backend:` A store reference to use as the canonical uncached source of truth.
"""
@impl Store.Behaviours.Core
@spec setup(Store.options()) :: {:ok, state :: term} | {:stop, reason :: any}
def setup(opts) do
{:ok, %{frontend: Keyword.fetch!(opts, :frontend), backend: Keyword.fetch!(opts, :backend)}}
end
####
# Mnemonix.Store.Behaviours.Map
##
@impl Store.Behaviours.Map
@spec delete(Store.t(), Mnemonix.key()) :: Store.Server.instruction()
def delete(%Store{state: state} = store, key) do
%{frontend: frontend, backend: backend} = state
with ^frontend <- Mnemonix.delete(frontend, key),
^backend <- Mnemonix.delete(backend, key) do
{:ok, store}
end
end
@impl Store.Behaviours.Map
@spec fetch(Store.t(), Mnemonix.key()) ::
Store.Server.instruction({:ok, Mnemonix.value()} | :error)
def fetch(%Store{state: state} = store, key) do
%{frontend: frontend, backend: backend} = state
if value = Mnemonix.get(frontend, key) do
{:ok, store, {:ok, value}}
else
if value = Mnemonix.get(backend, key) do
with ^frontend <- Mnemonix.put(frontend, key, value) do
{:ok, store, {:ok, value}}
end
else
{:ok, store, :error}
end
end
end
@impl Store.Behaviours.Map
@spec put(Store.t(), Mnemonix.key(), Mnemonix.value()) :: Store.Server.instruction()
def put(%Store{state: state} = store, key, value) do
%{frontend: frontend, backend: backend} = state
with ^frontend <- Mnemonix.put(frontend, key, value),
^backend <- Mnemonix.put(backend, key, value) do
{:ok, store}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment