Cachex v0.8.2 Cachex

Cachex provides a straightforward interface for in-memory key/value storage.

Cachex is an extremely fast, designed for caching but also allowing for more general in-memory storage. The main goal of Cachex is achieve a caching implementation with a wide array of options, without sacrificing performance. Internally, Cachex is backed by ETS and Mnesia, allowing for an easy-to-use interface sitting upon extremely well tested tools.

Cachex comes with support for all of the following (amongst other things):

  • Time-based key expirations
  • Pre/post execution hooks
  • Statistics gathering
  • Multi-layered caching/key fallbacks
  • Distribution to remote nodes
  • Transactions and row locking
  • Asynchronous write operations

All features are optional to allow you to tune based on the throughput needed. See start_link/2 for further details about how to configure these options and example usage.

Summary

Functions

Removes all key/value pairs from the cache

Determines the current size of the unexpired keyspace

Decrements a key directly in the cache

Removes a value from the cache

Checks whether the cache is empty

Determines whether a given key exists inside the cache

Sets a TTL on a key in the cache in milliseconds

Updates the expiration time on a given cache entry to expire at the time provided

Retrieves a value from the cache using a given key

Updates a value in the cache, feeding any existing values into an update function

Increments a key directly in the cache

Basic initialization phase, being passed arguments by the Supervisor

Retrieves all keys from the cache, and returns them as an (unordered) list

Removes a TTL on a given document

Triggers a mass deletion of all expired keys

Refreshes the TTL for the provided key. This will reset the TTL to begin from the current time

Sets a value in the cache against a given key

Determines the total size of the cache

Initialize the Mnesia table and supervision tree for this cache

Retrieves the statistics of a cache

Takes a key from the cache

Returns the TTL for a cache entry in milliseconds

Updates a value in the cache. Unlike get_and_update/4, this does a blind overwrite

Types

options :: [{atom, any}]
status :: :ok | :error | :missing

Functions

clear(cache, options \\ [])

Specs

clear(atom, options) :: {status, true | false}

Removes all key/value pairs from the cache.

This function returns a tuple containing the total number of keys removed from the internal cache. This is equivalent to running size/2 before running clear/2.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.clear(:my_cache)
{ :ok, 1 }

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.clear(:my_cache, async: true)
{ :ok, true }
count(cache, options \\ [])

Specs

count(atom, options) :: {status, number}

Determines the current size of the unexpired keyspace.

Unlike size/2, this ignores keys which should have expired. Due to this taking potentially expired keys into account, it is far more expensive than simply calling size/2 and should only be used when completely necessary.

Examples

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.set(:my_cache, "key2", "value2")
iex> Cachex.set(:my_cache, "key3", "value3")
iex> Cachex.count(:my_cache)
{ :ok, 3 }
decr(cache, key, options \\ [])

Specs

decr(atom, any, options) :: {status, number}

Decrements a key directly in the cache.

This operation is an internal mutation, and as such any set TTL persists - i.e. it is not refreshed on this operation.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.
  • :amount - an amount to decrement by. This will default to 1.
  • :initial - if the key does not exist, it will be initialized to this amount. Defaults to 0.

Examples

iex> Cachex.set(:my_cache, "my_key", 10)
iex> Cachex.decr(:my_cache, "my_key")
{ :ok, 9 }

iex> Cachex.decr(:my_cache, "my_key", async: true)
{ :ok, true }

iex> Cachex.set(:my_cache, "my_new_key", 10)
iex> Cachex.decr(:my_cache, "my_new_key", amount: 5)
{ :ok, 5 }

iex> Cachex.decr(:my_cache, "missing_key", amount: 5, initial: 0)
{ :ok, -5 }
del(cache, key, options \\ [])

Specs

del(atom, any, options) :: {status, true | false}

Removes a value from the cache.

This will return { :ok, true } regardless of whether a key has been removed or not. The true value can be thought of as “is value is no longer present?”.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.del(:my_cache, "key")
{ :ok, true }

iex> Cachex.del(:my_cache, "key", async: true)
{ :ok, true }
empty?(cache, options \\ [])

Specs

empty?(atom, options) :: {status, true | false}

Checks whether the cache is empty.

This operates based on keys living in the cache, regardless of whether they should have expired previously or not. Internally this is just sugar for checking if size/2 returns 0.

Examples

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.empty?(:my_cache)
{ :ok, false }

iex> Cachex.clear(:my_cache)
{ :ok, 1 }

iex> Cachex.empty?(:my_cache)
{ :ok, true }
exists?(cache, key, options \\ [])

Specs

exists?(atom, any, options) :: {status, true | false}

Determines whether a given key exists inside the cache.

This only determines if the key lives in the keyspace of the cache. Note that this determines existence within the bounds of TTLs; this means that if a key doesn’t “exist”, it may still be occupying memory in the cache.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.exists?(:my_cache, "key")
{ :ok, true }

iex> Cachex.exists?(:my_cache, "missing_key")
{ :ok, false }
expire(cache, key, expiration, options \\ [])

Specs

expire(atom, any, number, options) :: {status, true | false}

Sets a TTL on a key in the cache in milliseconds.

If the key does not exist in the cache, you will receive a result indicating this.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.expire(:my_cache, "key", :timer.seconds(5))
{ :ok, true }

iex> Cachex.expire(:my_cache, "missing_key", :timer.seconds(5))
{ :missing, false }

iex> Cachex.expire(:my_cache, "key", :timer.seconds(5), async: true)
{ :ok, true }

iex> Cachex.expire(:my_cache, "missing_key", :timer.seconds(5), async: true)
{ :ok, true }
expire_at(cache, key, timestamp, options \\ [])

Specs

expire_at(atom, binary, number, options) :: {status, true | false}

Updates the expiration time on a given cache entry to expire at the time provided.

If the key does not exist in the cache, you will receive a result indicating this. If the expiration date is in the past, the key will be immediately evicted when this function is called.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.expire_at(:my_cache, "key", 1455728085502)
{ :ok, true }

iex> Cachex.expire_at(:my_cache, "missing_key", 1455728085502)
{ :missing, false }

iex> Cachex.expire_at(:my_cache, "key", 1455728085502, async: true)
{ :ok, true }

iex> Cachex.expire_at(:my_cache, "missing_key", 1455728085502, async: true)
{ :ok, true }
get(cache, key, options \\ [])

Specs

get(atom, any, options) :: {status | :loaded, any}

Retrieves a value from the cache using a given key.

Options

  • :fallback - a fallback function for multi-layered caches, overriding any default fallback functions. The value returned by this fallback is placed in the cache against the provided key, before being returned to the user.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.get(:my_cache, "key")
{ :ok, "value" }

iex> Cachex.get(:my_cache, "missing_key")
{ :missing, nil }

iex> Cachex.get(:my_cache, "missing_key", fallback: &(String.reverse/1))
{ :loaded, "yek_gnissim" }
get_and_update(cache, key, update_function, options \\ [])

Specs

get_and_update(atom, any, function, options) :: {status | :loaded, any}

Updates a value in the cache, feeding any existing values into an update function.

This operation is an internal mutation, and as such any set TTL persists - i.e. it is not refreshed on this operation.

Options

  • :fallback - a fallback function for multi-layered caches, overriding any default fallback functions. The value returned by this fallback is passed into the update function.

Examples

iex> Cachex.set(:my_cache, "key", [2])
iex> Cachex.get_and_update(:my_cache, "key", &([1|&1]))
{ :ok, [1, 2] }

iex> Cachex.get_and_update(:my_cache, "missing_key", &(["value"|&1]), fallback: &(String.reverse/1))
{ :loaded, [ "value", "yek_gnissim" ] }
incr(cache, key, options \\ [])

Specs

incr(atom, any, options) :: {status, number}

Increments a key directly in the cache.

This operation is an internal mutation, and as such any set TTL persists - i.e. it is not refreshed on this operation.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.
  • :amount - an amount to increment by. This will default to 1.
  • :initial - if the key does not exist, it will be initialized to this amount before being modified. Defaults to 0.

Examples

iex> Cachex.set(:my_cache, "my_key", 10)
iex> Cachex.incr(:my_cache, "my_key")
{ :ok, 11 }

iex> Cachex.incr(:my_cache, "my_key", async: true)
{ :ok, true }

iex> Cachex.set(:my_cache, "my_new_key", 10)
iex> Cachex.incr(:my_cache, "my_new_key", amount: 5)
{ :ok, 15 }

iex> Cachex.incr(:my_cache, "missing_key", amount: 5, initial: 0)
{ :ok, 5 }
init(options)

Specs

init(options) :: {status, {any}}

Basic initialization phase, being passed arguments by the Supervisor.

This function sets up the Mnesia table and options are parsed before being used to setup the internal workers. Workers are then given to supervise/2.

keys(cache, options \\ [])

Specs

keys(atom, options) :: [any]

Retrieves all keys from the cache, and returns them as an (unordered) list.

Examples

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.set(:my_cache, "key2", "value2")
iex> Cachex.set(:my_cache, "key3", "value3")
iex> Cachex.keys(:my_cache)
{ :ok, [ "key2", "key1", "key3" ] }

iex> Cachex.clear(:my_cache)
iex> Cachex.keys(:my_cache)
{ :ok, [] }
persist(cache, key, options \\ [])

Specs

persist(atom, any, options) :: {status, true | false}

Removes a TTL on a given document.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "key", "value", ttl: 1000)
iex> Cachex.persist(:my_cache, "key")
{ :ok, true }

iex> Cachex.persist(:my_cache, "missing_key")
{ :missing, false }

iex> Cachex.persist(:my_cache, "missing_key", async: true)
{ :ok, true }
purge(cache, options \\ [])

Specs

purge(atom, options) :: {status, number}

Triggers a mass deletion of all expired keys.

This can be used to implement custom eviction policies rather than relying on the internal policy. Be careful though, calling purge/2 manually will result in the purge firing inside the main process rather than inside the TTL worker.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.purge(:my_cache)
{ :ok, 15 }

iex> Cachex.purge(:my_cache, async: true)
{ :ok, true }
refresh(cache, key, options \\ [])

Specs

refresh(atom, any, options) :: {status, true | false}

Refreshes the TTL for the provided key. This will reset the TTL to begin from the current time.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "my_key", "my_value", ttl: :timer.seconds(5))
iex> :timer.sleep(4)
iex> Cachex.refresh(:my_cache, "my_key")
iex> Cachex.ttl(:my_cache, "my_key")
{ :ok, 5000 }

iex> Cachex.refresh(:my_cache, "missing_key")
{ :missing, false }

iex> Cachex.refresh(:my_cache, "my_key", async: true)
{ :ok, true }

iex> Cachex.refresh(:my_cache, "missing_key", async: true)
{ :ok, true }
set(cache, key, value, options \\ [])

Specs

set(atom, any, any, options) :: {status, true | false}

Sets a value in the cache against a given key.

This will overwrite any value that was previously set against the provided key, and overwrite any TTLs which were already set.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.
  • :ttl - a time-to-live for the provided key/value pair, overriding any default ttl. This value should be in milliseconds.

Examples

iex> Cachex.set(:my_cache, "key", "value")
{ :ok, true }

iex> Cachex.set(:my_cache, "key", "value", async: true)
{ :ok, true }

iex> Cachex.set(:my_cache, "key", "value", ttl: :timer.seconds(5))
{ :ok, true }
size(cache, options \\ [])

Specs

size(atom, options) :: {status, number}

Determines the total size of the cache.

This includes any expired but unevicted keys. For a more representation which doesn’t include expired keys, use count/2.

Examples

iex> Cachex.set(:my_cache, "key1", "value1")
iex> Cachex.set(:my_cache, "key2", "value2")
iex> Cachex.set(:my_cache, "key3", "value3")
iex> Cachex.size(:my_cache)
{ :ok, 3 }
start_link(options \\ [], supervisor_options \\ [])

Specs

start_link(options, options) :: {atom, pid}

Initialize the Mnesia table and supervision tree for this cache.

We also allow the user to define their own options for the cache. We start a Supervisor to look after all internal workers backing the cache, in order to make sure everything is fault-tolerant.

Options

Required

  • name

    The name of the cache you’re creating, typically an atom.

    Cachex.start_link([ name: :my_cache ])

Optional

  • ets_opts

    A list of options to pass to the ETS table initialization.

    Cachex.start_link([ name: :my_cache, ets_opts: [ { :write_concurrency, false } ] ])
  • default_fallback

    A default fallback implementation to use when dealing with multi-layered caches. This function is called with a key which has no value, in order to allow loading from a different location.

    Cachex.start_link([ name: :my_cache, default_fallback: fn(key) ->
      generate_value(key)
    end])
  • default_ttl

    A default expiration time to place on any keys inside the cache (this can be overridden when a key is set). This value is in milliseconds.

    Cachex.start_link([ name: :my_cache, default_ttl: :timer.seconds(1) ])
  • fallback_args

    A list of arguments which can be passed to your fallback functions for multi-layered caches. The fallback function receives [key] ++ args, so make sure you configure your args appropriately. This can be used to pass through things such as clients and connections.

    Cachex.start_link([ name: :my_cache, fallback_args: [redis_client] ])
    Cachex.get(:my_cache, "key", fallback: fn(key, redis_client) ->
      redis_client.get(key)
    end)
  • hooks

    A list of hooks which will be executed either before or after a Cachex action has taken place. These hooks should be instances of Cachex.Hook and implement the hook behaviour. An example hook can be found in Cachex.Stats.

    hook = %Cachex.Hook{ module: MyHook, type: :post } Cachex.start_link([ name: :my_cache, hooks: [hook] ])

  • nodes

    A list of nodes that the store should replicate to. Using this does not automatically enable transactions; they need to be enabled separately.

    Cachex.start_link([ name: :my_cache, nodes: [node()] ])
  • record_stats

    Whether you wish this cache to record usage statistics or not. This has only minor overhead due to being implemented as an asynchronous hook (roughly 1µ/op). Stats can be retrieve from a running cache by using stats/1.

    Cachex.start_link([ name: :my_cache, record_stats: true ])
  • remote

    Whether to use remote behaviours or not. This means that all writes go through Mnesia rather than straight to ETS (and as such there is a slowdown). This is automatically set to true if you have set :nodes to a list of nodes other than just [node()].

    Cachex.start_link([ name: :my_cache, remote: true ])
  • transactional

    Whether to implement actions using a transactional interface. Transactions ensure row locks on all operations and are naturally a lot slower than when not in use. This implementation is likely not needed unless it’s mission-critical to distribute and you have a lot of writes going to the same keys. Note that this is at least 10x slower than when set to false.

    Cachex.start_link([ name: :my_cache, transactional: true ])
  • ttl_interval

    Keys are purged on a schedule (defaults to once a second). This value can be changed to customize the schedule that keys are purged on. Be aware that if a key is accessed when it should have expired, but has not yet been purged, it will be removed at that time. The purge runs in a separate process so it doesn’t have a negative effect on the application, but it may make sense to lower the frequency if you don’t have many keys expiring at one time. This value is set in milliseconds.

    Cachex.start_link([ name: :my_cache, ttl_interval: :timer.seconds(5) ])
stats(cache, options \\ [])

Specs

stats(atom, options) :: {status, %{}}

Retrieves the statistics of a cache.

If statistics gathering is not enabled, an error is returned.

Examples

iex> Cachex.stats(:my_cache)
{:ok,
 %{creationDate: 1455690638577, evictionCount: 0, expiredCount: 0, hitCount: 0,
   missCount: 0, opCount: 0, requestCount: 0, setCount: 0}}

iex> Cachex.stats(:cache_with_no_stats)
{ :error, "Stats not enabled for cache with ref ':cache_with_no_stats'" }
take(cache, key, options \\ [])

Specs

take(atom, any, options) :: {status, any}

Takes a key from the cache.

This is equivalent to running get/3 followed by del/3 in a single action.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.take(:my_cache, "key")
{ :ok, "value" }

iex> Cachex.get(:my_cache, "key")
{ :missing, nil }

iex> Cachex.take(:my_cache, "missing_key")
{ :missing, nil }
ttl(cache, key, options \\ [])

Specs

ttl(atom, any, options) :: {status, number}

Returns the TTL for a cache entry in milliseconds.

Examples

iex> Cachex.ttl(:my_cache, "my_key")
{ :ok, 13985 }

iex> Cachex.ttl(:my_cache, "missing_key")
{ :missing, nil }
update(cache, key, value, options \\ [])

Specs

update(atom, any, any, options) :: {status, any}

Updates a value in the cache. Unlike get_and_update/4, this does a blind overwrite.

This operation is an internal mutation, and as such any set TTL persists - i.e. it is not refreshed on this operation.

Options

  • :async - whether to wait on a response from the server, or to execute in the background.

Examples

iex> Cachex.set(:my_cache, "key", "value")
iex> Cachex.get(:my_cache, "key")
{ :ok, "value" }

iex> Cachex.update(:my_cache, "key", "new_value")
iex> Cachex.get(:my_cache, "key")
{ :ok, "new_value" }

iex> Cachex.update(:my_cache, "key", "final_value", async: true)
iex> Cachex.get(:my_cache, "key")
{ :ok, "final_value" }

iex> Cachex.update(:my_cache, "missing_key", "new_value")
{ :missing, false }