Nebulex v1.0.1 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
end
Could be configured with:
config :my_app, MyCache,
version_generator: MyCache.VersionGenerator,
stats: true,
gc_interval: 3600
Most 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
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
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
entries() View Source
Cache entries
key()
View Source
key() :: any()
key() :: any()
Object key
object()
View Source
object() :: Nebulex.Object.t()
object() :: Nebulex.Object.t()
Cache object
opts()
View Source
opts() :: Keyword.t()
opts() :: Keyword.t()
Cache action options
return() View Source
Return alternatives (value is the default)
t()
View Source
t() :: module()
t() :: module()
value()
View Source
value() :: any()
value() :: any()
Object value
Link to this section Callbacks
__adapter__()
View Source
__adapter__() :: Nebulex.Adapter.t()
__adapter__() :: Nebulex.Adapter.t()
Returns the adapter tied to the cache.
add(key, value, opts) View Source
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}
add!(key, value, opts) View Source
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")
add_or_replace!(key, value, opts) View Source
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")
all(query, opts) View Source (optional)
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.
config()
View Source
config() :: Keyword.t()
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.
delete(key, opts) View Source
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_key
OnConflict
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
expire(key, ttl) View Source
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
flush()
View Source
flush() :: :ok
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)
get(key, opts) View Source
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)
nil
OnConflict
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
get!(key, opts) View Source
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)
get_and_update(key, function, opts) View Source
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)
get_many(list, opts) View Source
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}
has_key?(key) View Source
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
in_transaction?()
View Source
(optional)
in_transaction?() :: boolean()
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)
init(config) View Source (optional)
A callback executed when the cache starts or when configuration is read.
object_info(key, attr) View Source
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
replace(key, value, opts) View Source
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"}
replace!(key, value, opts) View Source
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")
set(key, value, opts) View Source
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)
nil
OnConflict
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
set_many(entries, opts) View Source
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]}
size()
View Source
size() :: integer()
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
start_link(opts) View Source
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.
stop(pid, timeout) View Source
Shuts down the cache represented by the given pid.
stream(query, opts) View Source (optional)
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.
take(key, opts) View Source
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}
take!(key, opts) View Source
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)
transaction(function, opts) View Source (optional)
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]
update(key, initial, function, opts) View Source
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)
update_counter(key, incr, opts) View Source
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)