View Source Nebulex.Cache behaviour (Nebulex v2.6.3)

Cache's main interface; defines the cache abstraction layer which is highly inspired by Ecto.

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 MyApp.Cache do
  use Nebulex.Cache,
    otp_app: :my_app,
    adapter: Nebulex.Adapters.Local
end

Could be configured with:

config :my_app, MyApp.Cache,
  backend: :shards,
  gc_interval: :timer.hours(12),
  max_size: 1_000_000,
  allocated_memory: 2_000_000_000,
  gc_cleanup_min_timeout: :timer.seconds(10),
  gc_cleanup_max_timeout: :timer.minutes(10)

Most of the configuration that goes into the config is specific to the adapter. For this particular example, you can check Nebulex.Adapters.Local for more information. In spite of this, the following configuration values are shared across all adapters:

  • :name - The name of the Cache supervisor process.

  • :telemetry_prefix - It is recommend for adapters to publish events using the Telemetry library. By default, the telemetry prefix is based on the module name, so if your module is called MyApp.Cache, the prefix will be [:my_app, :cache]. See the "Telemetry events" section to see what events recommended for the adapters to publish.. Note that if you have multiple caches, you should keep the :telemetry_prefix consistent for each of them and use the :cache and/or :name (in case of a named or dynamic cache) properties in the event metadata for distinguishing between caches.

  • :telemetry - An optional flag to tell the adapters whether Telemetry events should be emitted or not. Defaults to true.

  • :stats - Boolean to define whether or not the cache will provide stats. Defaults to false. Each adapter is responsible for providing stats by implementing Nebulex.Adapter.Stats behaviour. See the "Stats" section below.

Telemetry events

Similar to Ecto or Phoenix, Nebulex also provides built-in Telemetry events applied to all caches, and cache adapter-specific events.

Nebulex built-in events

The following events are emitted by all Nebulex caches:

  • [:nebulex, :cache, :init] - it is dispatched whenever a cache starts. The measurement is a single system_time entry in native unit. The metadata is the :cache and all initialization options under :opts.

Adapter-specific events

It is recommend the adapters to publish certain Telemetry events listed below. Those events will use the :telemetry_prefix outlined above which defaults to [:my_app, :cache].

For instance, to receive all events published by a cache called MyApp.Cache, one could define a module:

defmodule MyApp.Telemetry do
  def handle_event(
        [:my_app, :cache, :command, event],
        measurements,
        metadata,
        config
      ) do
    case event do
      :start ->
        # Handle start event ...

      :stop ->
        # Handle stop event ...

      :exception ->
        # Handle exception event ...
    end
  end
end

Then, in the Application.start/2 callback, attach the handler to this event using a unique handler id:

:telemetry.attach(
  "my-app-handler-id",
  [:my_app, :cache, :command],
  &MyApp.Telemetry.handle_event/4,
  %{}
)

See the telemetry documentation for more information.

The following are the events you should expect from Nebulex. All examples below consider a cache named MyApp.Cache:

[:my_app, :cache, :command, :start]

This event should be invoked on every cache call sent to the adapter before the command logic is executed.

The :measurements map will include the following:

  • :system_time - The current system time in native units from calling: System.system_time().

A Telemetry :metadata map including the following fields. Each cache adapter may emit different information here. For built-in adapters, it will contain:

  • :adapter_meta - The adapter metadata.
  • :function_name - The name of the invoked adapter function.
  • :args - The arguments of the invoked adapter function, omitting the first argument, since it is the adapter metadata already included into the event's metadata.

[:my_app, :cache, :command, :stop]

This event should be invoked on every cache call sent to the adapter after the command logic is executed.

The :measurements map will include the following:

  • :duration - The time spent executing the cache command. The measurement is given in the :native time unit. You can read more about it in the docs for System.convert_time_unit/3.

A Telemetry :metadata map including the following fields. Each cache adapter may emit different information here. For built-in adapters, it will contain:

  • :adapter_meta - The adapter metadata.
  • :function_name - The name of the invoked adapter function.
  • :args - The arguments of the invoked adapter function, omitting the first argument, since it is the adapter metadata already included into the event's metadata.
  • :result - The command result.

[:my_app, :cache, :command, :exception]

This event should be invoked when an error or exception occurs while executing the cache command.

The :measurements map will include the following:

  • :duration - The time spent executing the cache command. The measurement is given in the :native time unit. You can read more about it in the docs for System.convert_time_unit/3.

A Telemetry :metadata map including the following fields. Each cache adapter may emit different information here. For built-in adapters, it will contain:

  • :adapter_meta - The adapter metadata.
  • :function_name - The name of the invoked adapter function.
  • :args - The arguments of the invoked adapter function, omitting the first argument, since it is the adapter metadata already included into the event's metadata.
  • :kind - The type of the error: :error, :exit, or :throw.
  • :reason - The reason of the error.
  • :stacktrace - The stacktrace.

NOTE: The events outlined above are the recommended for the adapters to dispatch. However, it is highly recommended to review the used adapter documentation to ensure it is fully compatible with these events, perhaps differences, or perhaps also additional events.

Stats

Stats are provided by the adapters by implementing the optional behaviour Nebulex.Adapter.Stats. This behaviour exposes a callback to return the current cache stats. Nevertheless, the behaviour brings with a default implementation using Erlang counters, which is used by the local built-in adapter (Nebulex.Adapters.Local).

One can enable the stats by setting the option :stats to true. For example, in the configuration file:

config :my_app, MyApp.Cache,
  stats: true,
  ...

Remember to check if the underlying adapter implements the Nebulex.Adapter.Stats behaviour.

See Nebulex.Cache.stats/0 for more information.

Dispatching stats via Telemetry

It is possible to emit Telemetry events for the current stats via Nebulex.Cache.dispatch_stats/1, but it has to be invoked explicitly; Nebulex does not emit this Telemetry event automatically. But it is very easy to emit this event using :telemetry_poller.

For example, one can define a custom pollable measurement:

:telemetry_poller.start_link(
  measurements: [
    {MyApp.Cache, :dispatch_stats, []},
  ],
  # configure sampling period - default is :timer.seconds(5)
  period: :timer.seconds(30),
  name: :my_cache_stats_poller
)

Or you can also start the :telemetry_poller process along with your application supervision tree:

def start(_type, _args) do
  my_cache_stats_poller_opts = [
    measurements: [
      {MyApp.Cache, :dispatch_stats, []},
    ],
    period: :timer.seconds(30),
    name: :my_cache_stats_poller
  ]

  children = [
    {MyApp.Cache, []},
    {:telemetry_poller, my_cache_stats_poller_opts}
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

See Nebulex Telemetry Guide for more information.

Distributed topologies

Nebulex provides the following adapters for distributed topologies:

These adapters work more as wrappers for an existing local adapter and provide the distributed topology on top of it. Optionally, you can set the adapter for the primary cache storage with the option :primary_storage_adapter. Defaults to Nebulex.Adapters.Local.

Summary

Types

Cache entries

Cache entry key

Cache action options

t()

Cache entry value

Callbacks

Returns the adapter tied to the cache.

Returns the default key generator applied only when using "declarative annotation-based caching" via Nebulex.Caching.Decorators.

Fetches all entries from cache matching the given query.

Returns the adapter configuration stored in the :otp_app environment.

Counts all entries in cache matching the given query.

Decrements the counter stored at key by the given amount.

Deletes the entry in Cache for a specific key.

Deletes all entries matching the given query. If query is nil, then all entries in the cache are deleted.

Emits a telemetry event when called with the current stats count.

Dumps a cache to the given file path.

Returns true if the given key exists and the new ttl was successfully updated, otherwise, false is returned.

flush() deprecated

Flushes the cache and returns the number of evicted keys.

Gets a value from Cache where the key matches the given key.

Similar to get/2 but raises KeyError if key is not found.

Returns a map with all the key-value pairs in the Cache where the key is in keys.

Gets the value from key and updates it, all in one pass.

Returns the atom name or pid of the current cache (based on Ecto dynamic repo).

Returns whether the given key exists in the Cache.

Returns true if the current process is inside a transaction.

Increments the counter stored at key by the given amount.

A callback executed when the cache starts or when configuration is read.

Loads a dumped cache from the given path.

Puts the given value under key into the Cache.

Puts the given entries (key/value pairs) into the Cache. It replaces existing values with new values (just as regular put).

Sets the dynamic cache to be used in further commands (based on Ecto dynamic repo).

Puts the given value under key into the cache, only if it does not already exist.

Similar to put_new/3 but raises Nebulex.KeyAlreadyExistsError if the key already exists.

Puts the given entries (key/value pairs) into the cache. It will not perform any operation at all even if just a single key already exists.

Alters the entry stored under key, but only if the entry already exists into the Cache.

Similar to replace/3 but raises KeyError if key is not found.

size() deprecated

Returns the total number of cached entries.

Starts a supervision and return {:ok, pid} or just :ok if nothing needs to be done.

Returns Nebulex.Stats.t() with the current stats values.

Shuts down the cache.

Similar to all/2 but returns a lazy enumerable that emits all entries from the cache matching the given query.

Returns and removes the value associated with key in the Cache. If the key does not exist, then nil is returned.

Similar to take/2 but raises KeyError if key is not found.

Returns true if the given key exists and the last access time was successfully updated, otherwise, false is returned.

Runs the given function inside a transaction.

Returns the remaining time-to-live for the given key. If the key does not exist, then nil is returned.

Updates the cached key with the given function.

Invokes the given function fun for the dynamic cache name_or_pid.

For the dynamic cache name_or_pid, invokes the given function name fun from module with the list of arguments args.

Types

@type entries() :: map() | [{key(), value()}]

Cache entries

@type key() :: any()

Cache entry key

@type opts() :: Keyword.t()

Cache action options

@type t() :: module()
@type value() :: any()

Cache entry value

Callbacks

@callback __adapter__() :: Nebulex.Adapter.t()

Returns the adapter tied to the cache.

Link to this callback

__default_key_generator__()

View Source
@callback __default_key_generator__() :: Nebulex.Caching.KeyGenerator.t()

Returns the default key generator applied only when using "declarative annotation-based caching" via Nebulex.Caching.Decorators.

Sometimes you may want to set a different key generator when using declarative caching. By default, the key generator is set to Nebulex.Caching.SimpleKeyGenerator. You can change the default key generator at compile time with:

use Nebulex.Cache, default_key_generator: MyKeyGenerator

See Nebulex.Caching.Decorators and Nebulex.Caching.KeyGenerator for more information.

Link to this callback

all(query, opts)

View Source (optional)
@callback all(query :: term(), opts()) :: [any()]

Fetches all entries from cache matching the given query.

May raise Nebulex.QueryError if query validation fails.

Query values

There are two types of query values. The ones shared and implemented by all adapters and the ones that are adapter specific.

Common queries

The following query values are shared and/or supported for all adapters:

  • nil - Returns a list with all cached entries based on the :return option.

Adapter-specific queries

The query value depends entirely on the adapter implementation; it could any term. Therefore, it is highly recommended to see adapters' documentation for more information about building queries. For example, the built-in Nebulex.Adapters.Local adapter uses :ets.match_spec() for queries, as well as other pre-defined ones like :unexpired and :expired.

Options

  • :return - Tells the query what to return from the matched entries. See the possible values in the "Query return option" section below. The default depends on the adapter, for example, the default for the built-in adapters is :key. This option is supported by the built-in adapters, but it is recommended to see the adapter's documentation to confirm its compatibility with this option.

See the configured adapter documentation for more runtime options.

Query return option

The following are the possible values for the :return option:

  • :key - Returns a list only with the keys.
  • :value - Returns a list only with the values.
  • :entry - Returns a list of Nebulex.Entry.t/0.
  • {:key, :value} - Returns a list of tuples in the form {key, value}.

See adapters documentation to confirm what of these options are supported and what other added.

Example

Populate the cache with some entries:

iex> :ok = Enum.each(1..5, &MyCache.put(&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 them as key/value pairs:

iex> MyCache.all(nil, return: {:key, :value})
[{1, 2}, {2, 4}, {3, 6}, {4, 8}, {5, 10}]

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 review adapters documentation. For instance, the built-in Nebulex.Adapters.Local adapter supports nil | :unexpired | :expired | :ets.match_spec() as query value.

Examples

Additional built-in queries for Nebulex.Adapters.Local adapter:

iex> unexpired = MyCache.all(:unexpired)
iex> expired = MyCache.all(:expired)

If we are using Nebulex.Adapters.Local adapter, the stored entry tuple {:entry, key, value, touched, ttl}, then the match spec could be something like:

iex> spec = [
...>   {{:entry, :"$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}]
@callback config() :: Keyword.t()

Returns the adapter configuration stored in the :otp_app environment.

If the init/1 callback is implemented in the cache, it will be invoked.

Link to this callback

count_all(query, opts)

View Source (optional)
@callback count_all(query :: term(), opts()) :: integer()

Counts all entries in cache matching the given query.

It returns the count of the matched entries.

If query is nil (the default), then the total number of cached entries is returned.

May raise Nebulex.QueryError if query validation fails.

Query values

See all/2 callback for more information about the query values.

Example

Populate the cache with some entries:

iex> :ok = Enum.each(1..5, &MyCache.put(&1, &1 * 2))

Count all entries in cache:

iex> MyCache.count_all()
5

Count all entries that match with the given query assuming we are using Nebulex.Adapters.Local adapter:

iex> query = [{{:_, :"$1", :"$2", :_, :_}, [{:>, :"$2", 5}], [true]}]
iex> MyCache.count_all(query)

For the local adapter you can use Ex2ms to build the match specs much easier.

Additional built-in queries for Nebulex.Adapters.Local adapter:

iex> unexpired = MyCache.count_all(:unexpired)
iex> expired = MyCache.count_all(:expired)
@callback decr(key(), amount :: integer(), opts()) :: integer()

Decrements the counter stored at key by the given amount.

If amount < 0 (negative), the value is incremented by that amount instead (opposite to incr/3).

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

  • :default - If key is not present in Cache, the default value is inserted as initial value of key before the it is incremented. Defaults to 0.

See the configured adapter documentation for more runtime options.

Examples

iex> MyCache.decr(:a)
-1

iex> MyCache.decr(:a, 2)
-3

iex> MyCache.decr(:a, -1)
-2

iex> MyCache.decr(:missing_key, 2, default: 10)
8
@callback delete(key(), opts()) :: :ok

Deletes the entry in Cache for a specific key.

See the configured adapter documentation for runtime options.

Example

iex> MyCache.put(:a, 1)
:ok

iex> MyCache.delete(:a)
:ok

iex> MyCache.get(:a)
nil

iex> MyCache.delete(:non_existent_key)
:ok
Link to this callback

delete_all(query, opts)

View Source (optional)
@callback delete_all(query :: term(), opts()) :: integer()

Deletes all entries matching the given query. If query is nil, then all entries in the cache are deleted.

It returns the number of deleted entries.

May raise Nebulex.QueryError if query validation fails.

See the configured adapter documentation for runtime options.

Query values

See all/2 callback for more information about the query values.

Example

Populate the cache with some entries:

iex> :ok = Enum.each(1..5, &MyCache.put(&1, &1 * 2))

Delete all (with default params):

iex> MyCache.delete_all()
5

Delete all entries that match with the given query assuming we are using Nebulex.Adapters.Local adapter:

iex> query = [{{:_, :"$1", :"$2", :_, :_}, [{:>, :"$2", 5}], [true]}]
iex> MyCache.delete_all(query)

For the local adapter you can use Ex2ms to build the match specs much easier.

Additional built-in queries for Nebulex.Adapters.Local adapter:

iex> unexpired = MyCache.delete_all(:unexpired)
iex> expired = MyCache.delete_all(:expired)
Link to this callback

dispatch_stats(opts)

View Source (optional)
@callback dispatch_stats(opts()) :: :ok

Emits a telemetry event when called with the current stats count.

The telemetry :measurements map will include the same as Nebulex.Stats.t()'s measurements. For example:

  • :evictions - Current evictions count.
  • :expirations - Current expirations count.
  • :hits - Current hits count.
  • :misses - Current misses count.
  • :updates - Current updates count.
  • :writes - Current writes count.

The telemetry :metadata map will include the same as Nebulex.Stats.t()'s metadata by default. For example:

  • :cache - The cache module, or the name (if an explicit name has been given to the cache).

Additionally, you can add your own metadata fields by given the option :metadata.

Options

  • :event_prefix – The prefix of the telemetry event. Defaults to [:nebulex, :cache].

  • :metadata – A map with additional metadata fields. Defaults to %{}.

Examples

iex> MyCache.dispatch_stats()
:ok

iex> MyCache.Stats.dispatch_stats(
...>   event_prefix: [:my_cache],
...>   metadata: %{tag: "tag1"}
...> )
:ok

NOTE: Since :telemetry is an optional dependency, when it is not defined, a default implementation is provided without any logic, just returning :ok.

Link to this callback

dump(path, opts)

View Source (optional)
@callback dump(path :: Path.t(), opts()) :: :ok | {:error, term()}

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 review the documentation of the adapter to be used. The built-in adapters inherit the default implementation from Nebulex.Adapter.Persistence, hence, review the available options there.

Examples

Populate the cache with 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
@callback expire(key(), ttl :: timeout()) :: boolean()

Returns true if the given key exists and the new ttl was successfully updated, otherwise, false is returned.

Examples

iex> MyCache.put(:a, 1)
:ok

iex> MyCache.expire(:a, 5)
true

iex> MyCache.expire(:a, :infinity)
true

iex> MyCache.ttl(:b, 5)
false
This callback is deprecated. Use delete_all/2 instead.
@callback flush() :: integer()

Flushes the cache and returns the number of evicted keys.

Examples

iex> :ok = Enum.each(1..5, &MyCache.put(&1, &1))
iex> MyCache.flush()
5

iex> MyCache.size()
0
@callback get(key(), opts()) :: value()

Gets a value from Cache where the key matches the given key.

Returns nil if no result was found.

See the configured adapter documentation for runtime options.

Example

iex> MyCache.put("foo", "bar")
:ok

iex>  MyCache.get("foo")
"bar"

iex> MyCache.get(:non_existent_key)
nil
@callback get!(key(), opts()) :: value()

Similar to get/2 but raises KeyError if key is not found.

See the configured adapter documentation for runtime options.

Example

MyCache.get!(:a)
@callback get_all(keys :: [key()], opts()) :: map()

Returns a map with all the key-value pairs in the Cache where the key is in keys.

If keys contains keys that are not in the Cache, they're simply ignored.

See the configured adapter documentation for runtime options.

Example

iex> MyCache.put_all([a: 1, c: 3])
:ok

iex> MyCache.get_all([:a, :b, :c])
%{a: 1, c: 3}
Link to this callback

get_and_update(key, function, opts)

View Source
@callback get_and_update(key(), (value() -> {current_value, new_value} | :pop), opts()) ::
  {current_value, new_value}
when current_value: value(), new_value: value()

Gets the 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 current 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 current value returned by fun and the new updated value under key.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

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}
@callback get_dynamic_cache() :: atom() | pid()

Returns the atom name or pid of the current cache (based on Ecto dynamic repo).

See also put_dynamic_cache/1.

@callback has_key?(key()) :: boolean()

Returns whether the given key exists in the Cache.

Examples

iex> MyCache.put(:a, 1)
:ok

iex> MyCache.has_key?(:a)
true

iex> MyCache.has_key?(:b)
false
Link to this callback

in_transaction?()

View Source (optional)
@callback 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)
@callback incr(key(), amount :: integer(), opts()) :: integer()

Increments the counter stored at key by the given amount.

If amount < 0 (negative), the value is decremented by that amount instead.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

  • :default - If key is not present in Cache, the default value is inserted as initial value of key before the it is incremented. Defaults to 0.

See the configured adapter documentation for more runtime options.

Examples

iex> MyCache.incr(:a)
1

iex> MyCache.incr(:a, 2)
3

iex> MyCache.incr(:a, -1)
2

iex> MyCache.incr(:missing_key, 2, default: 10)
12
@callback init(config :: Keyword.t()) :: {:ok, Keyword.t()} | :ignore

A callback executed when the cache starts or when configuration is read.

Link to this callback

load(path, opts)

View Source (optional)
@callback load(path :: Path.t(), opts()) :: :ok | {:error, term()}

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 review the documentation of the adapter to be used. Similarly, the built-in adapters inherit the default implementation from Nebulex.Adapter.Persistence, hence, review the available options there.

Examples

Populate the cache with 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")
:ok
@callback put(key(), value(), opts()) :: :ok

Puts the given value under key into the Cache.

If key already holds an entry, it is overwritten. Any previous time to live associated with the key is discarded on successful put operation.

By default, nil values are skipped, which means they are not stored; the call to the adapter is bypassed.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Example

iex> MyCache.put("foo", "bar")
:ok

If the value is nil, then it is not stored (operation is skipped):

iex> MyCache.put("foo", nil)
:ok

Put key with time-to-live:

iex> MyCache.put("foo", "bar", ttl: 10_000)
:ok

Using Nebulex.Time for TTL:

iex> MyCache.put("foo", "bar", ttl: :timer.hours(1))
:ok

iex> MyCache.put("foo", "bar", ttl: :timer.minutes(1))
:ok

iex> MyCache.put("foo", "bar", ttl: :timer.seconds(1))
:ok
@callback put_all(entries(), opts()) :: :ok

Puts the given entries (key/value pairs) into the Cache. It replaces existing values with new values (just as regular put).

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Example

iex> MyCache.put_all(apples: 3, bananas: 1)
:ok

iex> MyCache.put_all(%{apples: 2, oranges: 1}, ttl: 10_000)
:ok

Ideally, this operation should be atomic, so all given keys are put at once. But it depends purely on the adapter's implementation and the backend used internally by the adapter. Hence, it is recommended to review the adapter's documentation.

@callback put_dynamic_cache(atom() | pid()) :: atom() | pid()

Sets the dynamic cache to be used in further commands (based on Ecto dynamic repo).

There might be cases where we want to have different cache instances but accessing them through the same cache module. By default, when you call MyApp.Cache.start_link/1, it will start a cache with the name MyApp.Cache. But it is also possible to start multiple caches by using a different name for each of them:

MyApp.Cache.start_link(name: :cache1)
MyApp.Cache.start_link(name: :cache2, backend: :shards)

You can also start caches without names by explicitly setting the name to nil:

MyApp.Cache.start_link(name: nil, backend: :shards)

NOTE: There may be adapters requiring the :name option anyway, therefore, it is highly recommended to see the adapter's documentation you want to use.

However, once the cache is started, it is not possible to interact directly with it, since all operations through MyApp.Cache are sent by default to the cache named MyApp.Cache. But you can change the default cache at compile-time:

use Nebulex.Cache, default_dynamic_cache: :cache_name

Or anytime at runtime by calling put_dynamic_cache/1:

MyApp.Cache.put_dynamic_cache(:another_cache_name)

From this moment on, all future commands performed by the current process will run on :another_cache_name.

Link to this callback

put_new(key, value, opts)

View Source
@callback put_new(key(), value(), opts()) :: boolean()

Puts the given value under key into the cache, only if it does not already exist.

Returns true if a value was set, otherwise, false is returned.

By default, nil values are skipped, which means they are not stored; the call to the adapter is bypassed.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Example

iex> MyCache.put_new("foo", "bar")
true

iex> MyCache.put_new("foo", "bar")
false

If the value is nil, it is not stored (operation is skipped):

iex> MyCache.put_new("other", nil)
true
Link to this callback

put_new!(key, value, opts)

View Source
@callback put_new!(key(), value(), opts()) :: true

Similar to put_new/3 but raises Nebulex.KeyAlreadyExistsError if the key already exists.

See put_new/3 for general considerations and options.

Example

iex> MyCache.put_new!("foo", "bar")
true
Link to this callback

put_new_all(entries, opts)

View Source
@callback put_new_all(entries(), opts()) :: boolean()

Puts the given entries (key/value pairs) into the cache. It will not perform any operation at all even if just a single key already exists.

Returns true if all entries were successfully set. It returns false if no key was set (at least one key already existed).

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Example

iex> MyCache.put_new_all(apples: 3, bananas: 1)
true

iex> MyCache.put_new_all(%{apples: 3, oranges: 1}, ttl: 10_000)
false

Ideally, this operation should be atomic, so all given keys are put at once. But it depends purely on the adapter's implementation and the backend used internally by the adapter. Hence, it is recommended to review the adapter's documentation.

Link to this callback

replace(key, value, opts)

View Source
@callback replace(key(), value(), opts()) :: boolean()

Alters the entry stored under key, but only if the entry already exists into the Cache.

Returns true if a value was set, otherwise, false is returned.

By default, nil values are skipped, which means they are not stored; the call to the adapter is bypassed.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Example

iex> MyCache.replace("foo", "bar")
false

iex> MyCache.put_new("foo", "bar")
true

iex> MyCache.replace("foo", "bar2")
true

Update current value and TTL:

iex> MyCache.replace("foo", "bar3", ttl: 10_000)
true
Link to this callback

replace!(key, value, opts)

View Source
@callback replace!(key(), value(), opts()) :: true

Similar to replace/3 but raises KeyError if key is not found.

See replace/3 for general considerations and options.

Example

iex> MyCache.replace!("foo", "bar")
true
This callback is deprecated. Use count_all/2 instead.
@callback size() :: integer()

Returns the total number of cached entries.

Examples

iex> :ok = Enum.each(1..10, &MyCache.put(&1, &1))
iex> MyCache.size()
10

iex> :ok = Enum.each(1..5, &MyCache.delete(&1))
iex> MyCache.size()
5
@callback start_link(opts()) ::
  {:ok, pid()} | {:error, {:already_started, pid()}} | {:error, term()}

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.

@callback stats() :: Nebulex.Stats.t() | nil

Returns Nebulex.Stats.t() with the current stats values.

If the stats are disabled for the cache, then nil is returned.

Example

iex> MyCache.stats()
%Nebulex.Stats{
  measurements: {
    evictions: 0,
    expirations: 0,
    hits: 0,
    misses: 0,
    updates: 0,
    writes: 0
  },
  metadata: %{}
}
@callback stop(timeout()) :: :ok

Shuts down the cache.

Link to this callback

stream(query, opts)

View Source (optional)
@callback stream(query :: term(), opts()) :: Enum.t()

Similar to all/2 but returns a lazy enumerable that emits all entries from the cache matching the given query.

If query is nil, then all entries in cache match and are returned when the stream is evaluated; based on the :return option.

May raise Nebulex.QueryError if query validation fails.

Query values

See all/2 callback for more information about the query values.

Options

  • :return - Tells the query what to return from the matched entries. See the possible values in the "Query return option" section below. The default depends on the adapter, for example, the default for the built-in adapters is :key. This option is supported by the built-in adapters, but it is recommended to see the adapter's documentation to confirm its compatibility with this option.

  • :page_size - Positive integer (>= 1) that defines the page size internally used by the adapter for paginating the results coming back from the cache's backend. Defaults to 20; it's unlikely this will ever need changing.

See the configured adapter documentation for more runtime options.

Query return option

The following are the possible values for the :return option:

  • :key - Returns a list only with the keys.
  • :value - Returns a list only with the values.
  • :entry - Returns a list of Nebulex.Entry.t/0.
  • {:key, :value} - Returns a list of tuples in the form {key, value}.

See adapters documentation to confirm what of these options are supported and what other added.

Examples

Populate the cache with some entries:

iex> :ok = Enum.each(1..5, &MyCache.put(&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> nil |> MyCache.stream(return: :value, page_size: 3) |> Enum.to_list()
[2, 4, 6, 8, 10]

Stream all entries and return them as key/value pairs:

iex> nil |> MyCache.stream(return: {:key, :value}) |> Enum.to_list()
[{1, 2}, {2, 4}, {3, 6}, {4, 8}, {5, 10}]

Additional built-in queries for Nebulex.Adapters.Local adapter:

iex> unexpired_stream = MyCache.stream(:unexpired)
iex> expired_stream = MyCache.stream(:expired)

If we are using Nebulex.Adapters.Local adapter, the stored entry tuple {:entry, key, value, touched, ttl}, then the match spec could be something like:

iex> spec = [
...>   {{:entry, :"$1", :"$2", :_, :_},
...>   [{:>, :"$2", 5}], [{{:"$1", :"$2"}}]}
...> ]
iex> MyCache.stream(spec, page_size: 100) |> 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: 100) |> Enum.to_list()
[{3, 6}, {4, 8}, {5, 10}]
@callback take(key(), opts()) :: value()

Returns and removes the value associated with key in the Cache. If the key does not exist, then nil is returned.

If key is nil, the call to the adapter is bypassed, and nil is returned.

See the configured adapter documentation for runtime options.

Examples

iex> MyCache.put(:a, 1)
:ok

iex> MyCache.take(:a)
1

iex> MyCache.take(:a)
nil
@callback take!(key(), opts()) :: value()

Similar to take/2 but raises KeyError if key is not found.

See take/2 for general considerations and options.

Example

MyCache.take!(:a)
@callback touch(key()) :: boolean()

Returns true if the given key exists and the last access time was successfully updated, otherwise, false is returned.

Examples

iex> MyCache.put(:a, 1)
:ok

iex> MyCache.touch(:a)
true

iex> MyCache.ttl(:b)
false
Link to this callback

transaction(opts, function)

View Source (optional)
@callback transaction(opts(), function :: (... -> any())) :: term()

Runs the given function inside a transaction.

A successful transaction returns the value returned by the function.

See the configured adapter documentation for runtime options.

Examples

MyCache.transaction fn ->
  alice = MyCache.get(:alice)
  bob = MyCache.get(:bob)
  MyCache.put(:alice, %{alice | balance: alice.balance + 100})
  MyCache.put(:bob, %{bob | balance: bob.balance + 100})
end

Locking only the involved key (recommended):

MyCache.transaction [keys: [:alice, :bob]], fn ->
  alice = MyCache.get(:alice)
  bob = MyCache.get(:bob)
  MyCache.put(:alice, %{alice | balance: alice.balance + 100})
  MyCache.put(:bob, %{bob | balance: bob.balance + 100})
end
@callback ttl(key()) :: timeout() | nil

Returns the remaining time-to-live for the given key. If the key does not exist, then nil is returned.

Examples

iex> MyCache.put(:a, 1, ttl: 5000)
:ok

iex> MyCache.put(:b, 2)
:ok

iex> MyCache.ttl(:a)
_remaining_ttl

iex> MyCache.ttl(:b)
:infinity

iex> MyCache.ttl(:c)
nil
Link to this callback

update(key, initial, function, opts)

View Source
@callback update(key(), initial :: value(), (value() -> value()), opts()) :: value()

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. The initial value will not be passed through the update function.

Options

  • :ttl - (positive integer or :infinity) Defines the time-to-live (or expiry time) for the given key in milliseconds. Defaults to :infinity.

See the configured adapter documentation for more runtime options.

Examples

iex> MyCache.update(:a, 1, &(&1 * 2))
1

iex> MyCache.update(:a, 1, &(&1 * 2))
2
Link to this callback

with_dynamic_cache(name_or_pid, function)

View Source
@callback with_dynamic_cache(name_or_pid :: atom() | pid(), (... -> any())) :: term()

Invokes the given function fun for the dynamic cache name_or_pid.

Example

MyCache.with_dynamic_cache(:my_cache, fn ->
  MyCache.put("foo", "var")
end)

See get_dynamic_cache/0 and put_dynamic_cache/1.

Link to this callback

with_dynamic_cache(name_or_pid, module, fun, args)

View Source
@callback with_dynamic_cache(
  name_or_pid :: atom() | pid(),
  module(),
  fun :: atom(),
  args :: [term()]
) :: term()

For the dynamic cache name_or_pid, invokes the given function name fun from module with the list of arguments args.

Example

MyCache.with_dynamic_cache(:my_cache, Module, :some_fun, ["foo", "bar"])

See get_dynamic_cache/0 and put_dynamic_cache/1.