Nebulex v1.2.2 Nebulex.Cache behaviour View Source
Cache Main Interface.
A Cache maps to an underlying implementation, controlled by the adapter. For example, Nebulex ships with a default adapter that implements a local generational cache.
When used, the Cache expects the :otp_app and :adapter as options.
The :otp_app should point to an OTP application that has the Cache
configuration. For example, the Cache:
defmodule MyCache do
use Nebulex.Cache,
otp_app: :my_app,
adapter: Nebulex.Adapters.Local
endCould be configured with:
config :my_app, MyCache,
version_generator: MyCache.VersionGenerator,
stats: true,
gc_interval: 3600Most of the configuration that goes into the config is specific
to the adapter, so check Nebulex.Adapters.Local documentation
for more information. However, some configuration is shared across
all adapters, they are:
:version_generator- this option specifies the module that implements theNebulex.Object.Versioninterface. This interface defines only one callbackgenerate/1that is invoked by the adapters to generate new object versions. If this option is not set, then the version is set tonilby default.:stats- a compile-time option that specifies if cache statistics is enabled or not (defaults tofalse).
Pre/Post hooks
The cache can also provide its own pre/post hooks implementation; see
Nebulex.Hook behaviour. By default, pre/post hooks are empty lists,
but you can override the functions of Nebulex.Hook behaviour.
Additionally, it is possible to configure the mode how the hooks are evaluated (synchronous, asynchronous and pipeline).
For more information about the usage, check out Nebulex.Hook.
Shared options
Almost all of the Cache operations below accept the following options:
:return- Selects return type::value | :key | :object. If:value(the default) is set, only object's value is returned. If:keyset, only object's key is returned. If:objectis set,Nebulex.Object.t()is returned.:version- The version of the object on which the operation will take place. The version can be any term (default:nil).:ttl- Time To Live (TTL) or expiration time in seconds for a key (default::infinity).
Such cases will be explicitly documented as well as any extra option.
Extended API
Some adapters might extend the API with additional functions, therefore, it is important to check out adapters' documentation.
Link to this section Summary
Types
Cache entries
Object key
Cache object
Cache action options
Return alternatives (value is the default)
Object value
Callbacks
Returns the adapter tied to the cache.
Sets the given value under key into the cache, only if it does not
already exist.
Similar to add/3 but raises Nebulex.KeyAlreadyExistsError if the
key already exists.
When the key already exists, the cached value is replaced by value,
otherwise the object is created at fisrt time.
Fetches all entries from cache matching the given query.
Returns the adapter configuration stored in the :otp_app environment.
Deletes the entry in cache for a specific key.
Dumps a cache to the given file path.
Returns the expiry timestamp for the given key, if the timeout ttl
(in seconds) is successfully updated.
Flushes the cache.
Gets a value or object from Cache where the key matches the
given key.
Similar to get/2 but raises KeyError if key is not found.
Gets the object/value from key and updates it, all in one pass.
Returns a map with the values or objects (check :return option) for all
specified keys. For every key that does not hold a value or does not exist,
that key is simply ignored. Because of this, the operation never fails.
Returns whether the given key exists in cache.
Returns true if the current process is inside a transaction.
A callback executed when the cache starts or when configuration is read.
Loads a dumped cache from the given path.
Returns the information associated with attr for the given key,
or returns nil if key doesn't exist.
Alters the object stored under key, but only if the object already exists
into the cache.
Similar to replace/3 but raises KeyError if key is not found.
Sets the given value under key into the cache.
Sets the given entries, replacing existing ones, just as regular set.
Returns the total number of cached entries.
Starts a supervision and return {:ok, pid} or just :ok if nothing
needs to be done.
Shuts down the cache represented by the given pid.
Similar to all/2 but returns a lazy enumerable that emits all entries
from the cache matching the given query.
Returns and removes the object with key key in the cache.
Similar to take/2 but raises KeyError if key is not found.
Runs the given function inside a transaction.
Updates the cached key with the given function.
Updates (increment or decrement) the counter mapped to the given key.
Link to this section Types
Specs
Cache entries
Specs
key() :: any()
Object key
Specs
object() :: Nebulex.Object.t()
Cache object
Specs
opts() :: Keyword.t()
Cache action options
Specs
Return alternatives (value is the default)
Specs
t() :: module()
Specs
value() :: any()
Object value
Link to this section Callbacks
Specs
__adapter__() :: Nebulex.Adapter.t()
Returns the adapter tied to the cache.
Specs
Sets the given value under key into the cache, only if it does not
already exist.
If cache doesn't contain the given key, then {:ok, value} is returned.
If cache contains key, :error is returned.
Options
See the "Shared options" section at the module documentation.
For add operation, the option :version is ignored.
Example
iex> MyCache.add("foo", "bar")
{ok, "bar"}
iex> MyCache.add("foo", "bar")
:error
# if the value is nil, it is not stored (operation is skipped)
iex> MyCache.add("foo", nil)
{ok, nil} Specs
Similar to add/3 but raises Nebulex.KeyAlreadyExistsError if the
key already exists.
Options
See the "Shared options" section at the module documentation.
Example
MyCache.add!("foo", "bar") Specs
When the key already exists, the cached value is replaced by value,
otherwise the object is created at fisrt time.
Returns the replaced or created value when the function is completed
successfully. Because this function is a combination of replace/3
and add!/3 functions, there might be a race condition between them,
in that case the last operation add!/3 fails with
Nebulex.KeyAlreadyExistsError.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Example
MyCache.add_or_replace!("foo", "bar") Specs
Fetches all entries from cache matching the given query.
If the query is nil, it fetches all entries from cache; this is common
for all adapters. However, the query could be any other value, which
depends entirely on the adapter's implementation; check out the "Query"
section below.
May raise Nebulex.QueryError if query validation fails.
Options
See the "Shared options" section at the module documentation.
Note that for this function, option :version is ignored.
Example
# set some entries
iex> :ok = Enum.each(1..5, &MyCache.set(&1, &1 * 2))
# fetch all (with default params)
iex> MyCache.all()
[1, 2, 3, 4, 5]
# fetch all entries and return values
iex> MyCache.all(nil, return: :value)
[2, 4, 6, 8, 10]
# fetch all entries and return objects
iex> [%Nebulex.Object{} | _] = MyCache.all(nil, return: :object)
# fetch all entries that match with the given query
# assuming we are using Nebulex.Adapters.Local adapter
iex> query = [{{:"$1", :"$2", :_, :_}, [{:>, :"$2", 5}], [:"$1"]}]
iex> MyCache.all(query)
[3, 4, 5]Query
Query spec is defined by the adapter, hence, it is recommended to check out
adapters documentation. For instance, the built-in Nebulex.Adapters.Local
adapter supports nil | :all_unexpired | :all_expired | :ets.match_spec()
as query value.
Examples
# additional built-in queries for Nebulex.Adapters.Local adapter
iex> all_unexpired = MyCache.all(:all_unexpired)
iex> all_expired = MyCache.all(:all_expired)
# if we are using Nebulex.Adapters.Local adapter, the stored entry
# is a tuple {key, value, version, expire_at}, then the match spec
# could be something like:
iex> spec = [{{:"$1", :"$2", :_, :_}, [{:>, :"$2", 5}], [{{:"$1", :"$2"}}]}]
iex> MyCache.all(spec)
[{3, 6}, {4, 8}, {5, 10}]
# the same previous query but using Ex2ms
iex> import Ex2ms
Ex2ms
iex> spec =
...> fun do
...> {key, value, _, _} when value > 5 -> {key, value}
...> end
iex> MyCache.all(spec)
[{3, 6}, {4, 8}, {5, 10}]To learn more, check out adapters' documentation.
Specs
config() :: Keyword.t()
Returns the adapter configuration stored in the :otp_app environment.
If the c:init/2 callback is implemented in the cache, it will be invoked.
Specs
Deletes the entry in cache for a specific key.
If the key does not exist, returns the result according to the :return
option (for delete defaults to :key) but the cache is not altered.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- It may be one of:raise(the default),:nothing,:override. See the "OnConflict" section for more information.
Note that for this function :return option hasn't any effect
since it always returns the key either success or not.
Example
iex> MyCache.set(:a, 1)
1
iex> MyCache.delete(:a)
:a
iex> MyCache.get(:a)
nil
iex> MyCache.delete(:non_existent_key)
:non_existent_keyOnConflict
The :on_conflict option supports the following values:
:raise- raises if there is a conflicting key:nothing- ignores the error in case of conflicts:override- the command is executed ignoring the conflict, then the value on the existing key is deleted
Examples
# Set a value
iex> MyCache.set(:a, 1)
1
# Delete with an invalid version but do nothing on conflicts.
# Keep in mind that, although this returns successfully, the returned
# `key` does not reflect the data in the cache. For instance, in case
# of "on_conflict: :nothing", the returned `key` isn't deleted.
iex> MyCache.delete(:a, version: :invalid, on_conflict: :nothing)
:a
iex> MyCache.get(:a)
1
# Delete with the same invalid version but force to delete the current
# value on conflicts (if the entry exists).
iex> MyCache.delete(:a, version: :invalid, on_conflict: :override)
:a
iex> MyCache.get(:a)
nil Specs
Dumps a cache to the given file path.
Returns :ok if successful, or {:error, reason} if an error occurs.
Options
This operation relies entirely on the adapter implementation, which means the
options depend on each of them. For that reason, it is recommended to check
the documentation of the adapter to be used. The built-in adapters inherit
the default implementation from Nebulex.Adapter.Persistence, so check out
the available options there.
Examples
# set some entries
iex> entries = for x <- 1..10, into: %{}, do: {x, x}
iex> MyCache.set_many(entries)
:ok
# dump cache to a file
iex> MyCache.dump("my_cache")
:okTo learn more, check out adapters' documentation.
Specs
Returns the expiry timestamp for the given key, if the timeout ttl
(in seconds) is successfully updated.
If key doesn't exist, nil is returned.
Examples
iex> MyCache.set(:a, 1)
1
iex> MyCache.expire(:a, 5)
1540004049
iex> MyCache.expire(:a, :infinity)
:infinity
iex> MyCache.ttl(:b, 5)
nil Specs
flush() :: :ok
Flushes the cache.
Examples
iex> :ok = Enum.each(1..5, &MyCache.set(&1, &1))
iex> MyCache.flush()
:ok
iex> Enum.each(1..5, fn x ->
...> nil = MyCache.get(x)
...> end) Specs
Gets a value or object from Cache where the key matches the
given key.
Returns nil if no result was found.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- It may be one of:raise(the default),:nothing,:override. See the "OnConflict" section for more information.
Example
iex> MyCache.set("foo", "bar")
"bar"
iex> MyCache.get("foo")
"bar"
iex> MyCache.get("foo", return: :object)
%Nebulex.Object{key: "foo", value: "bar", version: nil, expire_at: nil}
iex> MyCache.get(:non_existent_key)
nilOnConflict
The :on_conflict option supports the following values:
:raise- raises if there is a conflicting key:nothing- ignores the error in case of conflicts:override- same effect as:nothing
Examples
# Set a value
iex> MyCache.set(:a, 1)
1
# Gets with an invalid version but do nothing on conflicts.
# Keep in mind that, although this returns successfully, the returned
# struct does not reflect the data in the Cache. For instance, in case
# of "on_conflict: :nothing", it returns the latest version of the
# cached object.
iex> %Nebulex.Object{value: 1} =
...> MyCache.get(:a, return: :object, version: :invalid, on_conflict: :nothing)
iex> MyCache.get(:a)
1 Specs
Similar to get/2 but raises KeyError if key is not found.
Options
See the "Shared options" section at the module documentation.
Example
MyCache.get!(:a) Specs
get_and_update(key(), (value() -> {get, update} | :pop), opts()) :: {get, update} when get: value(), update: return()
Gets the object/value from key and updates it, all in one pass.
fun is called with the current cached value under key (or nil
if key hasn't been cached) and must return a two-element tuple:
the "get" value (the retrieved value, which can be operated on before
being returned) and the new value to be stored under key. fun may
also return :pop, which means the current value shall be removed
from Cache and returned.
The returned value is a tuple with the "get" value returned by
fun and the new updated value under key.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Examples
# update nonexistent key
iex> MyCache.get_and_update(:a, fn current_value ->
...> {current_value, "value!"}
...> end)
{nil, "value!"}
# update existing key
iex> MyCache.get_and_update(:a, fn current_value ->
...> {current_value, "new value!"}
...> end)
{"value!", "new value!"}
# pop/remove value if exist
iex> MyCache.get_and_update(:a, fn _ -> :pop end)
{"new value!", nil}
# pop/remove nonexistent key
iex> MyCache.get_and_update(:b, fn _ -> :pop end)
{nil, nil}
# update existing key but returning the object
iex> {"hello", %Object{key: :a, value: "hello world"}} =
...> :a
...> |> MyCache.set!("hello", return: :key)
...> |> MyCache.get_and_update(fn current_value ->
...> {current_value, "hello world"}
...> end, return: :object) Specs
Returns a map with the values or objects (check :return option) for all
specified keys. For every key that does not hold a value or does not exist,
that key is simply ignored. Because of this, the operation never fails.
Options
See the "Shared options" section at the module documentation.
Note that for this function, option :version is ignored.
Example
iex> MyCache.set_many([a: 1, c: 3])
:ok
iex> MyCache.get_many([:a, :b, :c])
%{a: 1, c: 3} Specs
Returns whether the given key exists in cache.
Examples
iex> MyCache.set(:a, 1)
1
iex> MyCache.has_key?(:a)
true
iex> MyCache.has_key?(:b)
false Specs
in_transaction?() :: boolean()
Returns true if the current process is inside a transaction.
Examples
MyCache.in_transaction?
#=> false
MyCache.transaction(fn ->
MyCache.in_transaction? #=> true
end) Specs
A callback executed when the cache starts or when configuration is read.
Specs
Loads a dumped cache from the given path.
Returns :ok if successful, or {:error, reason} if an error occurs.
Options
Similar to dump/2, this operation relies entirely on the adapter
implementation, therefore, it is recommended to check the documentation
of the adapter to be used. Similarly, the built-in adapters inherit the
default implementation from Nebulex.Adapter.Persistence, so check out
the available options there.
Examples
# set some entries
iex> entries = for x <- 1..10, into: %{}, do: {x, x}
iex> MyCache.set_many(entries)
:ok
# dump cache to a file
iex> MyCache.dump("my_cache")
:ok
# load the cache from a file
iex> MyCache.load("my_cache")
:okTo learn more, check out adapters' documentation.
Specs
Returns the information associated with attr for the given key,
or returns nil if key doesn't exist.
If attr is not one of the allowed values, ArgumentError is raised.
The following values are allowed for attr:
:ttl- Returns the remaining time to live for the givenkey, if it has a timeout, otherwise,:infinityis returned.:version- Returns the current version for the givenkey.
Examples
iex> MyCache.set(:a, 1, ttl: 5)
1
iex> MyCache.set(:b, 2)
2
iex> MyCache.object_info(:a, :ttl)
5
iex> MyCache.ttl(:b)
:infinity
iex> MyCache.object_info(:a, :version)
nil Specs
Alters the object stored under key, but only if the object already exists
into the cache.
If cache contains the given key, then {:ok, return} is returned.
If cache doesn't contain key, :error is returned.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Example
iex> MyCache.replace("foo", "bar")
:error
iex> MyCache.set("foo", "bar")
"bar"
# update only current value
iex> MyCache.replace("foo", "bar2")
{:ok, "bar2"}
# update current value and TTL
iex> MyCache.replace("foo", "bar3", ttl: 10)
{:ok, "bar3"} Specs
Similar to replace/3 but raises KeyError if key is not found.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Example
MyCache.replace!("foo", "bar") Specs
Sets the given value under key into the cache.
If key already holds an object, it is overwritten. Any previous
time to live associated with the key is discarded on successful
set operation.
Returns either the value, key or the object, depending on :return
option, if the action is completed successfully.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- It may be one of:raise(the default),:nothing,:override. See the "OnConflict" section for more information.
Example
iex> MyCache.set("foo", "bar")
"bar"
iex> MyCache.set("foo", "bar", ttl: 10, return: :object)
%Nebulex.Object{key: "foo", value: "bar", version: nil, expire_at: 1540004049}
# if the value is nil, then it is not stored (operation is skipped)
iex> MyCache.set("foo", nil)
nilOnConflict
The :on_conflict option supports the following values:
:raise- raises if there is a conflicting key:nothing- ignores the error in case of conflicts:override- the command is executed ignoring the conflict, then the value on the existing key is replaced with the givenvalue
Examples
iex> %Nebulex.Object{version: v1} = MyCache.set(:a, 1, return: :object)
iex> MyCache.set(:a, 2, version: v1)
2
# Set with the same key and wrong version but do nothing on conflicts.
# Keep in mind in case of "on_conflict: :nothing", the returned object
# is the current cached object, if there is one
iex> MyCache.set(:a, 3, version: v1, on_conflict: :nothing)
2
# Set with the same key and wrong version but replace the current
# value on conflicts.
iex> MyCache.set(:a, 3, version: v1, on_conflict: :override)
3 Specs
Sets the given entries, replacing existing ones, just as regular set.
Returns :ok if the all entries were successfully set, otherwise
{:error, failed_keys}, where failed_keys contains the keys that
could not be set.
Ideally, this operation should be atomic, so all given keys are set at once. But it depends purely on the adapter's implementation and the backend used internally by the adapter. Hence, it is recommended to checkout the adapter's documentation.
Options
See the "Shared options" section at the module documentation.
Note that for this function, option :version is ignored.
Example
iex> MyCache.set_many(apples: 3, bananas: 1)
:ok
iex> MyCache.set_many(%{"apples" => 1, "bananas" => 3})
:ok
# set a custom list of objects
iex> MyCache.set_many([
...> %Nebulex.Object{key: :apples, value: 1},
...> %Nebulex.Object{key: :bananas, value: 2, expire_at: 5}
...> ])
:ok
# for some reason `:c` couldn't be set, so we got an error
iex> MyCache.set_many([a: 1, b: 2, c: 3], ttl: 1000)
{:error, [:c]} Specs
size() :: integer()
Returns the total number of cached entries.
Examples
iex> :ok = Enum.each(1..10, &MyCache.set(&1, &1))
iex> MyCache.size()
10
iex> :ok = Enum.each(1..5, &MyCache.delete(&1))
iex> MyCache.size()
5 Specs
Starts a supervision and return {:ok, pid} or just :ok if nothing
needs to be done.
Returns {:error, {:already_started, pid}} if the cache is already
started or {:error, term} in case anything else goes wrong.
Options
See the configuration in the moduledoc for options shared between adapters, for adapter-specific configuration see the adapter's documentation.
Specs
Shuts down the cache represented by the given pid.
Specs
Similar to all/2 but returns a lazy enumerable that emits all entries
from the cache matching the given query.
May raise Nebulex.QueryError if query validation fails.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:page_size- Positive integer (>= 1) that defines the page size for the stream (defaults to10).
Examples
# set some entries
iex> :ok = Enum.each(1..5, &MyCache.set(&1, &1 * 2))
# stream all (with default params)
iex> MyCache.stream() |> Enum.to_list()
[1, 2, 3, 4, 5]
# stream all entries and return values
iex> MyCache.stream(nil, return: :value, page_size: 3) |> Enum.to_list()
[2, 4, 6, 8, 10]
# stream all entries and return objects
iex> stream = MyCache.stream(nil, return: :object, page_size: 3)
iex> [%Nebulex.Object{} | _] = Enum.to_list(stream)
# additional built-in queries for Nebulex.Adapters.Local adapter
all_unexpired_stream = MyCache.stream(:all_unexpired)
all_expired_stream = MyCache.stream(:all_expired)
# if we are using Nebulex.Adapters.Local adapter, the stored entry
# is a tuple {key, value, version, expire_at}, then the match spec
# could be something like:
iex> spec = [{{:"$1", :"$2", :_, :_}, [{:>, :"$2", 5}], [{{:"$1", :"$2"}}]}]
iex> MyCache.stream(spec, page_size: 3) |> Enum.to_list()
[{3, 6}, {4, 8}, {5, 10}]
# the same previous query but using Ex2ms
iex> import Ex2ms
Ex2ms
iex> spec =
...> fun do
...> {key, value, _, _} when value > 5 -> {key, value}
...> end
iex> spec |> MyCache.stream(page_size: 3) |> Enum.to_list()
[{3, 6}, {4, 8}, {5, 10}]To learn more, check out adapters' documentation.
Specs
Returns and removes the object with key key in the cache.
Returns nil if no result was found.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Examples
iex> MyCache.set(:a, 1)
1
iex> MyCache.take(:a)
1
iex> MyCache.take(:a)
nil
iex> :a |> MyCache.set(1, return: :key) |> MyCache.take(return: :object)
%Nebulex.Object{key: :a, value: 1, version: nil, expire_at: nil} Specs
Similar to take/2 but raises KeyError if key is not found.
Options
See the "Shared options" section at the module documentation.
Example
MyCache.take!(:a) Specs
Runs the given function inside a transaction.
A successful transaction returns the value returned by the function.
Options
See the "Shared options" section at the module documentation.
Examples
MyCache.transaction fn ->
alice = MyCache.get(:alice)
bob = MyCache.get(:bob)
MyCache.set(:alice, %{alice | balance: alice.balance + 100})
MyCache.set(:bob, %{bob | balance: bob.balance + 100})
end
# locking only the involved key (recommended):
MyCache.transaction fn ->
alice = MyCache.get(:alice)
bob = MyCache.get(:bob)
MyCache.set(:alice, %{alice | balance: alice.balance + 100})
MyCache.set(:bob, %{bob | balance: bob.balance + 100})
end, keys: [:alice, :bob] Specs
Updates the cached key with the given function.
If key is present in Cache with value value, fun is invoked with
argument value and its result is used as the new value of key.
If key is not present in Cache, initial is inserted as the value
of key.
Options
Besides the "Shared options" section at the module documentation, it accepts:
:on_conflict- Same as callbackset/3.
Examples
iex> MyCache.update(:a, 1, &(&1 * 2))
1
iex> MyCache.update(:a, 1, &(&1 * 2))
2
iex> %Nebulex.Object{value: 4} =
...> MyCache.update(:a, 1, &(&1 * 2), return: :object) Specs
Updates (increment or decrement) the counter mapped to the given key.
If incr >= 0 then the current value is incremented by that amount,
otherwise the current value is decremented.
If incr is not a valid integer, then an ArgumentError exception
is raised.
Options
See the "Shared options" section at the module documentation.
Note that for this function, option :version is ignored.
Examples
iex> MyCache.update_counter(:a)
1
iex> MyCache.update_counter(:a, 2)
3
iex> MyCache.update_counter(:a, -1)
2
iex> %Nebulex.Object{key: :a, value: 2} =
...> MyCache.update_counter(:a, 0, return: :object)