Cashmere behaviour (Cashmere v0.1.0) View Source

This module provides the interface to work with Cashmere, a high performance in-memory caching solution.

To get started with Cashmere, you need to create a module that calls use Cashmere, like this:

defmodule MyApp.Cache do
  use Cashmere, purge_interval: _milliseconds = 100, partitions: 4
end

This way, MyApp.Cache becomes a Cashmere cache with four partitions. It comes with the child_spec/1 function that returns child specification that allows us to start MyApp.Cache directly under a supervision tree, and many other functions to work with the cache as documented in this module.

Usually you won't call child_spec/1 directly but just add the cache to the application supervision tree.

def start(_type, _args) do
  children = [
    MyApp.Cache,
    # ...
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end

There are a few configuration values available for use Cashmere:

  • purge_interval — (required) the interval in milliseconds when expired items in the cache are purged. Note that intervals are not exact, but at least as long as the interval is passed.
  • partitions — the amount of paritions of this cache. Defaults to 1.

Link to this section Summary

Types

Expiration time (in milliseconds or :infinity).

Callbacks

Returns a specification to start the cache under a supervisor.

Retrieves the value stored under key, invokes value_fetcher if not found, and puts the returned value in the cache under key, with the specified expiration.

Retrieves the value by a specific key from the cache.

Puts the given value under key in the cache, with the specified expiration.

Retrieves the value stored under key, invokes value_fetcher serializably if not found, and puts the returned value in the cache under key, with the specified expiration.

Link to this section Types

Specs

expiration() :: pos_integer() | :infinity

Expiration time (in milliseconds or :infinity).

Specs

key() :: any()

Specs

value() :: any()

Link to this section Callbacks

Specs

child_spec(options :: Keyword.t()) :: Supervisor.child_spec()

Returns a specification to start the cache under a supervisor.

See the "Child specification" section in the Supervisor module for more detailed information.

Link to this callback

dirty_read(key, expiration, value_fetcher)

View Source

Specs

dirty_read(
  key(),
  expiration(),
  value_fetcher :: (() -> {:ok, result} | {:error, reason})
) :: {:ok, result} | {:error, reason}
when result: value(), reason: any()

Retrieves the value stored under key, invokes value_fetcher if not found, and puts the returned value in the cache under key, with the specified expiration.

Note that, since value_fetcher will always be invoked in case of a cache miss, this function is subjected to cascading failures under very high load. Use read/3 if you need serializable invocation.

Examples

iex> MyApp.Cache.dirty_read(:name, 30_000, fn ->
...>   very_heavy_computation()
...> end)
{:ok, "cashmere"}

Specs

get(key()) :: {:ok, value()} | :error

Retrieves the value by a specific key from the cache.

Examples

iex> MyApp.Cache.get(:name)
{:ok, "cashmere"}
iex> MyApp.Cache.get(:does_not_exist)
:error
Link to this callback

put(key, value, expiration)

View Source

Specs

put(key(), value(), expiration()) :: :ok

Puts the given value under key in the cache, with the specified expiration.

To put a value that never expires, use :infinity for expiration.

Note that entries in the cache are purged periodically with the configured purge_interval, it's possible for the value to exist for a short while after the given expiration time.

Examples

iex> MyApp.Cache.put(:name, "cashmere", _30_seconds = 30_000)
:ok
Link to this callback

read(key, expiration, value_fetcher)

View Source

Specs

read(
  key(),
  expiration(),
  value_fetcher :: (() -> {:ok, result} | {:error, reason})
) ::
  {:ok, value() | result}
  | {:error, reason}
  | {:error, {:cache, :callback_failure}}
  | {:error, {:cache, :retry_failure}}
when result: value(), reason: any()

Retrieves the value stored under key, invokes value_fetcher serializably if not found, and puts the returned value in the cache under key, with the specified expiration.

"Serializably" means that there will be only one invocation of value_fetcher at a point in time, amongst many concurrent read/3 calls with the same key, in the current runtime instance. This can be used as a possible mitigation for cache stampedes under very high load, to help avoiding cascading failures under very high load when massive cache misses happen for hot keys.

Note that this function is subjected to some minor performance overhead. Most of the time when it is not necessary, consider using dirty_read/3.

There are several possible errors:

  • {:cache, :callback_failure} — the invocation of value_fetcher raised an exception.
  • {:cache, :retry_failure} — the invocation of value_fetcher succeeded but the value could not be retrieved.
  • reason — the invocation of value_fetcher returned an error with reason.

Examples

iex> MyApp.Cache.read(:name, 30_000, fn ->
...>   very_heavy_computation()
...> end)
{:ok, "cashmere"}