Last active
December 13, 2017 19:54
-
-
Save christhekeele/1c0dc69d23931c69e1d50fc2fe9ca842 to your computer and use it in GitHub Desktop.
An example meta store for Mnemonix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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