Nebulex.Cache behaviour (Nebulex v3.0.0-rc.1)

View Source

Cache abstraction layer inspired by Ecto.

A cache maps to an underlying in-memory storage controlled by the adapter. For example, Nebulex ships with an adapter that implements a local generational cache.

The cache expects the :otp_app and :adapter as options when used. The :otp_app should point to an OTP application with the cache configuration. See the compile time options for more information:

  • :otp_app (atom/0) - Required. The OTP application the cache configuration is under.

  • :adapter (module/0) - Required. The cache adapter module.

  • :default_dynamic_cache (atom/0) - Default dynamic cache for executing cache commands. Set to the defined cache module by default. For example, when you call MyApp.Cache.start_link/1, it will start a cache with the name MyApp.Cache.

    See the "Dynamic caches" section for more information.

  • :adapter_opts (keyword/0) - Specifies a list of options passed to the adapter in compilation time. The default value is [].

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,
  gc_interval: :timer.hours(12),
  max_size: 1_000_000,
  allocated_memory: 2_000_000_000,
  gc_memory_check_interval: :timer.seconds(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. Despite this, the following configuration values are shared across all adapters:

  • :name (atom/0 | {:via, reg_mod :: module(), via_name :: any()}) - The name of the supervisor process the cache is started under. Set to the defined cache module by default. For example, when you call MyApp.Cache.start_link/1, a cache named MyApp.Cache is started.

  • :telemetry (boolean/0) - A flag to determine whether to emit the Telemetry cache command events. The default value is true.

  • :telemetry_prefix (list of atom/0) - Nebulex emits cache 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 which events are emitted by Nebulex out-of-box.

    If you have multiple caches, keep the :telemetry_prefix consistent for each cache and use the :cache property within the :adapter_meta coming in the event metadata to distinguish between caches. For dynamic caches, you should additionally use the :name property. Alternatively, you can use different :telemetry_prefix values.

  • :bypass_mode (boolean/0) - If true, the cache calls are skipped by overwriting the configured adapter with Nebulex.Adapters.Nil when the cache starts. This option is handy for tests if you want to disable or bypass the cache while running the tests. The default value is false.

Shared options

All of the cache functions outlined in this module accept the following options:

  • :timeout (timeout/0) - The time in milliseconds to wait for a command to finish (:infinity to wait indefinitely). The default value is 5000. However, it may change depending on the adapter.

    Timeout option

    Despite being a shared option accepted by almost all cache functions, it is up to the adapter to support it.

  • :telemetry_event (list of atom/0) - The telemetry event name to dispatch the event under. Defaults to what is configured in the :telemetry_prefix option. See the "Telemetry events" section for more information.

  • :telemetry_metadata (map of term/0 keys and term/0 values) - Extra metadata to add to the Telemetry cache command events. These end up in the :extra_metadata metadata key of these events.

    See the "Telemetry events" section for more information.

Adapter-specific options

In addition to the shared options, each adapter can define its specific options. Therefore, Nebulex recommends reviewing the adapter's documentation.

Telemetry events

There are two types of telemetry events. The ones emitted by Nebulex and the ones that are adapter specific. The ones emitted by Nebulex are divided into two categories: cache lifecycle events and cache command events. Let us take a closer look at each of them.

Cache lifecycle events

All Nebulex caches emit the following events:

  • [:nebulex, :cache, :init] - It is dispatched whenever a cache starts. The only measurement is the current system time in native units from calling: System.system_time(). The :opts key in the metadata contains all initialization options.

    • Measurement: %{system_time: integer()}
    • Metadata: %{cache: module(), name: atom(), opts: keyword()}

Cache command events

When the option :telemetry is set to true (the default), Nebulex will emit Telemetry span events for each cache command. Those events will use the :telemetry_prefix outlined in the options above which defaults to [:my_app, :cache].

For instance, to receive all events published for the cache 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 ...
        IO.puts("Cache command started: #{inspect(metadata.command)}")
        IO.puts("Arguments: #{inspect(metadata.args)}")

      :stop ->
        # Handle stop event ...
        duration = measurements.duration
        IO.puts("Cache command completed: #{inspect(metadata.command)}")
        IO.puts("Duration: #{duration} native units")
        IO.puts("Result: #{inspect(metadata.result)}")

      :exception ->
        # Handle exception event ...
        IO.puts("Cache command failed: #{inspect(metadata.command)}")
        IO.puts("Error: #{inspect(metadata.reason)}")
        IO.puts("Stacktrace: #{inspect(metadata.stacktrace)}")
    end
  end
end

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

:telemetry.attach_many(
  "my-app-handler-id",
  [
    [:my_app, :cache, :command, :start],
    [:my_app, :cache, :command, :stop],
    [:my_app, :cache, :command, :exception]
  ],
  &MyApp.Telemetry.handle_event/4,
  :no_config
)

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 is emitted before a cache command 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:

  • :adapter_meta - The adapter metadata.
  • :command - The name of the invoked adapter's command.
  • :args - The arguments passed to the invoked adapter, except for the first one, since the adapter's metadata is available in the event's metadata.
  • :extra_metadata - Additional metadata through the runtime option :telemetry_metadata.

Example event data:

%{
  measurements: %{system_time: 1_678_123_456_789},
  metadata: %{
    adapter_meta: %{...},
    command: :put,
    args: ["key", "value", [ttl: :timer.seconds(10)]],
    extra_metadata: %{user_id: 123}
  }
}

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

This event is emitted after a cache command 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:

  • :adapter_meta - The adapter metadata.
  • :command - The name of the invoked adapter's command.
  • :args - The arguments passed to the invoked adapter, except for the first one, since the adapter's metadata is available in the event's metadata.
  • :extra_metadata - Additional metadata through the runtime option :telemetry_metadata.
  • :result - The command's result.

Example event data:

%{
  measurements: %{duration: 1_234_567},
  metadata: %{
    adapter_meta: %{...},
    command: :put,
    args: ["key", "value", [ttl: :timer.seconds(10)]],
    extra_metadata: %{user_id: 123},
    result: :ok
  }
}

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

This event is emitted when an error or exception occurs during the cache command execution.

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:

  • :adapter_meta - The adapter metadata.
  • :command - The name of the invoked adapter's command.
  • :args - The arguments passed to the invoked adapter, except for the first one, since the adapter's metadata is available in the event's metadata.
  • :extra_metadata - Additional metadata through the runtime option :telemetry_metadata.
  • :kind - The type of the error: :error, :exit, or :throw.
  • :reason - The reason of the error.
  • :stacktrace - Exception's stack trace.

Example event data:

%{
  measurements: %{duration: 1_234_567},
  metadata: %{
    adapter_meta: %{...},
    command: :put,
    args: ["key", "value", [ttl: :timer.seconds(10)]],
    extra_metadata: %{user_id: 123},
    kind: :error,
    reason: %Nebulex.KeyError{key: "key", reason: :not_found},
    stacktrace: [...]
  }
}

Adapter-specific events

Regardless of whether Nebulex emits the telemetry events outlined above or not, the adapters can and are free to expose their own, but they will be out of Nebulex's scope. Therefore, if you are interested in using specific adapter events, you should review the adapters' documentation.

Dynamic caches

Nebulex allows you to start multiple processes from the same cache module. This feature is typically useful when you want to have different cache instances but access them through the same cache module.

When you list a cache in your supervision tree, such as MyApp.Cache, it will start a supervision tree with a process named MyApp.Cache under the hood. By default, the process has the same name as the cache module. Hence, whenever you invoke a function in MyApp.Cache, such as MyApp.Cache.put/3, Nebulex will execute the command in the cache process named MyApp.Cache.

However, with Nebulex, you can start multiple processes from the same cache. The only requirement is that they must have different process names, like this:

children = [
  MyApp.Cache,
  {MyApp.Cache, name: MyApp.UsersCache}
]

Now you have two cache instances running: one is named MyApp.Cache, and the other one is named MyApp.UsersCache. You can tell Nebulex which process you want to use in your cache operations by calling:

MyApp.Cache.put_dynamic_cache(MyApp.Cache)
MyApp.Cache.put_dynamic_cache(MyApp.UsersCache)

Once you call MyApp.Cache.put_dynamic_cache(name), all invocations made on MyApp.Cache will use the cache instance denoted by name.

Nebulex also provides a handy function for invoking commands using dynamic caches: with_dynamic_cache/2.

MyApp.Cache.with_dynamic_cache(MyApp.UsersCache, fn ->
  # all commands here will use MyApp.UsersCache
  MyApp.Cache.put("u1", "joe")
  ...
end)

While these functions are handy, you may want to have the ability to pass the dynamic cache directly to the command, avoiding the boilerplate logic of using put_dynamic_cache/1 or with_dynamic_cache/2. From v3.0, all Cache API commands expose an extended callback version that admits a dynamic cache at the first argument, so you can directly interact with a cache instance.

MyApp.Cache.put(MyApp.UsersCache, "u1", "joe", ttl: :timer.hours(1))
MyApp.Cache.get(MyApp.UsersCache, "u1", nil, [])
MyApp.Cache.delete(MyApp.UsersCache, "u1", [])

This is another handy way to work with multiple cache instances through the same cache module.

Distributed topologies

One of the goals of Nebulex is also to provide the ability to set up distributed cache topologies, but this feature will depend on the adapters.

See "Nebulex Distributed".

Summary

User callbacks

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

Runtime API

Returns the adapter tied to the cache.

Returns the adapter configuration stored in the :otp_app environment.

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

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

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

Shuts down the cache.

Same as stop/1 but stops the cache instance given in the first argument dynamic_cache.

Invokes the function fun using the given dynamic cache.

KV API

Decrements the counter stored at key by the given amount and returns the current count as {:ok, count}.

Same as decr/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as decr/3 but raises an exception if an error occurs.

Same as decr!/4 but raises an exception if an error occurs.

Deletes the entry in the cache for a specific key.

Same as delete/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as delete/2 but raises an exception if an error occurs.

Same as delete!/3 but raises an exception if an error occurs.

Returns {:ok, true} if the given key exists and the new ttl is successfully updated; otherwise, {:ok, false} is returned.

Same as expire/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as expire/3 but raises an exception if an error occurs.

Same as expire!/4 but raises an exception if an error occurs.

Fetches the value for a specific key in the cache.

Same as fetch/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as fetch/2 but raises Nebulex.KeyError if the cache doesn't contain key or Nebulex.Error if another error occurs while executing the command.

Same as fetch/3 but raises Nebulex.KeyError if the cache doesn't contain key or Nebulex.Error if another error occurs while executing the command.

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

Same as get/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as get/3 but raises an exception if an error occurs.

Same as get!/4 but raises an exception if an error occurs.

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

Same as get_and_update/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as get_and_update/3 but raises an exception if an error occurs.

Same as get_and_update!/4 but raises an exception if an error occurs.

Determines if the cache contains an entry for the specified key.

Same as has_key?/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Increments the counter stored at key by the given amount and returns the current count as {:ok, count}.

Same as incr/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as incr/3 but raises an exception if an error occurs.

Same as incr!/4 but raises an exception if an error occurs.

Puts the given value under key into the cache.

Same as put/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as put/3 but raises an exception if an error occurs.

Same as put!/4 but raises an exception if an error occurs.

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

Same as put_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as put_all/2 but raises an exception if an error occurs.

Same as put_all!/3 but raises an exception if an error occurs.

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

Same as put_new/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as put_new/3 but raises an exception if an error occurs.

Same as put_new!/4 but raises an exception if an error occurs.

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

Same as put_new_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as put_new_all/2 but raises an exception if an error occurs.

Same as put_new_all!/3 but raises an exception if an error occurs.

Alters the entry stored under key, but only if the entry already exists in the cache.

Same as replace/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as replace/3 but raises an exception if an error occurs.

Same as replace!/4 but raises an exception if an error occurs.

Removes and returns the value associated with key in the cache.

Same as take/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as take/2 but raises an exception if an error occurs.

Same as take!/3 but raises an exception if an error occurs.

Returns {:ok, true} if the given key exists and the last access time is successfully updated; otherwise, {:ok, false} is returned.

Same as touch/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as touch/2 but raises an exception if an error occurs.

Same as touch!/3 but raises an exception if an error occurs.

Returns the remaining time-to-live for the given key.

Same as ttl/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as ttl/2 but raises an exception if an error occurs.

Same as ttl!/3 but raises an exception if an error occurs.

Updates the key in the cache with the given function.

Same as update/4, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as update/4 but raises an exception if an error occurs.

Same as update!/5 but raises an exception if an error occurs.

Query API

Counts all entries matching the query specified by the given query_spec.

Same as count_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as count_all/2 but raises an exception if an error occurs.

Same as count_all!/3 but raises an exception if an error occurs.

Deletes all entries matching the query specified by the given query_spec.

Same as delete_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as delete_all/2 but raises an exception if an error occurs.

Same as delete_all!/3 but raises an exception if an error occurs.

Fetches all entries from the cache matching the given query specified through the "query-spec".

Same as get_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as get_all/2 but raises an exception if an error occurs.

Same as get_all!/3 but raises an exception if an error occurs.

Similar to get_all/2, but returns a lazy enumerable that emits all entries matching the query specified by the given query_spec.

Same as stream/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as stream/2 but raises an exception if an error occurs.

Same as stream!/3 but raises an exception if an error occurs.

Transaction API

Returns {:ok, true} if the current process is inside a transaction; otherwise, {:ok, false} is returned.

Same as in_transaction?/1, but the command is executed on the cache instance given at the first argument dynamic_cache.

Runs the given function inside a transaction.

Same as transaction/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Info API

Returns {:ok, info} where info contains the requested cache information, as specified by the spec.

Same as info/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as info/2 but raises an exception if an error occurs.

Same as info/3 but raises an exception if an error occurs.

Observable API

Register a cache event listener event_listener.

Same as register_event_listener/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as register_event_listener/2 but raises an exception if an error occurs.

Un-register a cache event listener.

Same as unregister_event_listener/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

Same as unregister_event_listener/2 but raises an exception if an error occurs.

Same as unregister_event_listener/3 but raises an exception if an error occurs.

Types

Dynamic cache value

Cache entries

Common error type

Error type for the given reason

Proxy type to a cache event filter

Proxy type to a cache event listener

Fetch error reason

The data type for the cache information

The type for the info item's value

Info map

Specification key for the item(s) to include in the returned info

Cache entry key

Proxy type for generic Nebulex error

Ok/Error tuple with default error reasons

Cache action options

The data type for a query spec

t()

Cache type

Cache entry value

User callbacks

init(config)

(optional)
@callback init(config) :: {:ok, config} | :ignore when config: keyword()

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

Runtime API

__adapter__()

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

Returns the adapter tied to the cache.

config()

@callback config() :: keyword()

Returns the adapter configuration stored in the :otp_app environment.

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

get_dynamic_cache()

@callback get_dynamic_cache() :: dynamic_cache()

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

See also put_dynamic_cache/1.

put_dynamic_cache(dynamic_cache)

@callback put_dynamic_cache(dynamic_cache()) :: dynamic_cache()

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

There are cases where you may want to have different cache instances but access 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)

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

MyApp.Cache.start_link(name: nil)

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.

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.

Additionally, all cache commands optionally support passing the wanted dynamic cache (name or PID) as the first argument so you can o directly interact with a cache instance. See the "Dynamic caches" section in the module documentation for more information.

start_link(opts)

@callback start_link(opts()) ::
  {:ok, pid()} | {:error, {:already_started, pid()}} | {:error, any()}

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(opts)

@callback stop(opts()) :: :ok

Shuts down the cache.

Options

:timeout - It is an integer that specifies how many milliseconds to wait for the cache supervisor process to terminate, or the atom :infinity to wait indefinitely. Defaults to 5000. See Supervisor.stop/3.

See the "Shared options" section in the module documentation for more options.

stop(dynamic_cache, opts)

@callback stop(dynamic_cache(), opts()) :: :ok

Same as stop/1 but stops the cache instance given in the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

with_dynamic_cache(dynamic_cache, fun)

@callback with_dynamic_cache(dynamic_cache(), fun()) :: any()

Invokes the function fun using the given dynamic cache.

Example

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

See get_dynamic_cache/0 and put_dynamic_cache/1.

KV API

decr(key, amount, opts)

@callback decr(key(), amount :: integer(), opts()) :: ok_error_tuple(integer())

Decrements the counter stored at key by the given amount and returns the current count as {:ok, count}.

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

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

If the key doesn't exist, the TTL is set. Otherwise, only the counter value is updated, keeping the TTL set for the first time.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :default (integer/0) - If the key is not present in the cache, the default value is inserted as the key's initial value before it is incremented. The default value is 0.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.decr(:a)
{:ok, -1}
iex> MyCache.decr(:a, 2)
{:ok, -3}
iex> MyCache.decr(:a, -1)
{:ok, -2}

iex> MyCache.decr(:missing_key, 2, default: 10)
{:ok, 8}

decr(dynamic_cache, key, amount, opts)

@callback decr(dynamic_cache(), key(), amount :: integer(), opts()) ::
  ok_error_tuple(integer())

Same as decr/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.decr(MyCache1, :a, 1, [])
{:ok, -1}

decr!(key, amount, opts)

@callback decr!(key(), amount :: integer(), opts()) :: integer()

Same as decr/3 but raises an exception if an error occurs.

Examples

iex> MyCache.decr!(:a)
-1

decr!(dynamic_cache, key, amount, opts)

@callback decr!(dynamic_cache(), key(), amount :: integer(), opts()) :: integer()

Same as decr!/4 but raises an exception if an error occurs.

delete(key, opts)

@callback delete(key(), opts()) :: :ok | error_tuple()

Deletes the entry in the cache for a specific key.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.delete(:a)
:ok
iex> MyCache.get!(:a)
nil
iex> MyCache.delete(:nonexistent)
:ok

delete(dynamic_cache, key, opts)

@callback delete(dynamic_cache(), key(), opts()) :: :ok | error_tuple()

Same as delete/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.delete(MyCache1, :a, [])
:ok

delete!(key, opts)

@callback delete!(key(), opts()) :: :ok

Same as delete/2 but raises an exception if an error occurs.

delete!(dynamic_cache, key, opts)

@callback delete!(dynamic_cache(), key(), opts()) :: :ok

Same as delete!/3 but raises an exception if an error occurs.

expire(key, ttl, opts)

@callback expire(key(), ttl :: timeout(), opts()) :: ok_error_tuple(boolean())

Returns {:ok, true} if the given key exists and the new ttl is successfully updated; otherwise, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned; where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.expire(:a, :timer.hours(1))
{:ok, true}
iex> MyCache.expire(:a, :infinity)
{:ok, true}
iex> MyCache.expire(:b, 5)
{:ok, false}

expire(dynamic_cache, key, ttl, opts)

@callback expire(dynamic_cache(), key(), ttl :: timeout(), opts()) ::
  ok_error_tuple(boolean())

Same as expire/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.expire(MyCache1, :a, :timer.hours(1), [])
{:ok, false}

expire!(key, ttl, opts)

@callback expire!(key(), ttl :: timeout(), opts()) :: boolean()

Same as expire/3 but raises an exception if an error occurs.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.expire!(:a, :timer.hours(1))
true

expire!(dynamic_cache, key, ttl, opts)

@callback expire!(dynamic_cache(), key(), ttl :: timeout(), opts()) :: boolean()

Same as expire!/4 but raises an exception if an error occurs.

fetch(key, opts)

@callback fetch(key(), opts()) :: ok_error_tuple(value(), fetch_error_reason())

Fetches the value for a specific key in the cache.

If the cache contains the given key, then its value is returned in the shape of {:ok, value}.

If there's an error with executing the command, {:error, reason} is returned. reason is the cause of the error and can be Nebulex.KeyError if the cache does not contain key, Nebulex.Error otherwise.

Options

See the "Shared options" section in the module documentation for more options.

Examples

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

# Key not found error
iex> {:error, %Nebulex.KeyError{key: "bar"} = e} = MyCache.fetch("bar")
iex> e.reason
:not_found

fetch(dynamic_cache, key, opts)

@callback fetch(dynamic_cache(), key(), opts()) ::
  ok_error_tuple(value(), fetch_error_reason())

Same as fetch/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put("foo", "bar")
:ok
iex> MyCache.fetch(MyCache1, "foo", [])
{:ok, "bar"}

fetch!(key, opts)

@callback fetch!(key(), opts()) :: value()

Same as fetch/2 but raises Nebulex.KeyError if the cache doesn't contain key or Nebulex.Error if another error occurs while executing the command.

Examples

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

fetch!(dynamic_cache, key, opts)

@callback fetch!(dynamic_cache(), key(), opts()) :: value()

Same as fetch/3 but raises Nebulex.KeyError if the cache doesn't contain key or Nebulex.Error if another error occurs while executing the command.

get(key, default, opts)

@callback get(key(), default :: value(), opts()) :: ok_error_tuple(value())

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

If the cache contains the given key its value is returned as {:ok, value}.

If the cache does not contain key, {:ok, default} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put("foo", "bar")
:ok
iex> MyCache.get("foo")
{:ok, "bar"}
iex> MyCache.get(:nonexistent)
{:ok, nil}
iex> MyCache.get(:nonexistent, :default)
{:ok, :default}

get(dynamic_cache, key, default, opts)

@callback get(dynamic_cache(), key(), default :: value(), opts()) ::
  ok_error_tuple(value())

Same as get/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.get(MyCache1, "key", nil, [])
{:ok, nil}

get!(key, default, opts)

@callback get!(key(), default :: value(), opts()) :: value()

Same as get/3 but raises an exception if an error occurs.

get!(dynamic_cache, key, default, opts)

@callback get!(dynamic_cache(), key(), default :: value(), opts()) :: value()

Same as get!/4 but raises an exception if an error occurs.

get_and_update(key, function, opts)

@callback get_and_update(key(), (value() -> {current_value, new_value} | :pop), opts()) ::
  ok_error_tuple({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 the cache and returned.

This function returns:

  • {:ok, {current_value, new_value}} - The current_value is the current cached value and new_value the updated one returned by fun.

  • {:error, reason} - An error occurred executing the command. reason is the cause of the error.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :keep_ttl (boolean/0) - Indicates whether to retain the time to live associated with the key. Otherwise, the value in the :ttl option overwrites the existing one. The default value is false.

See the "Shared options" section in the module documentation for more options.

Examples

Update nonexistent key:

iex> MyCache.get_and_update(:a, fn current_value ->
...>   {current_value, "value!"}
...> end)
{:ok, {nil, "value!"}}

Update existing key:

iex> MyCache.get_and_update(:a, fn current_value ->
...>   {current_value, "new value!"}
...> end)
{:ok, {"value!", "new value!"}}

Pop/remove value if exist:

iex> MyCache.get_and_update(:a, fn _ -> :pop end)
{:ok, {"new value!", nil}}

Pop/remove nonexistent key:

iex> MyCache.get_and_update(:b, fn _ -> :pop end)
{:ok, {nil, nil}}

get_and_update(dynamic_cache, key, function, opts)

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

Same as get_and_update/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.get_and_update(MyCache1, :a, &{&1, "value!"}, [])
{:ok, {nil, "value!"}}

get_and_update!(key, function, opts)

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

Same as get_and_update/3 but raises an exception if an error occurs.

Examples

iex> MyCache.get_and_update!(:a, &{&1, "value!"})
{nil, "value!"}

get_and_update!(dynamic_cache, key, function, opts)

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

Same as get_and_update!/4 but raises an exception if an error occurs.

has_key?(key, opts)

@callback has_key?(key(), opts()) :: ok_error_tuple(boolean())

Determines if the cache contains an entry for the specified key.

More formally, it returns {:ok, true} if the cache contains the given key. If the cache doesn't contain key, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.has_key?(:a)
{:ok, true}
iex> MyCache.has_key?(:b)
{:ok, false}

has_key?(dynamic_cache, key, opts)

@callback has_key?(dynamic_cache(), key(), opts()) :: ok_error_tuple(boolean())

Same as has_key?/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.has_key?(MyCache1, :a, [])
{:ok, false}

incr(key, amount, opts)

@callback incr(key(), amount :: integer(), opts()) :: ok_error_tuple(integer())

Increments the counter stored at key by the given amount and returns the current count as {:ok, count}.

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

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

If the key doesn't exist, the TTL is set. Otherwise, only the counter value is updated, keeping the TTL set for the first time.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :default (integer/0) - If the key is not present in the cache, the default value is inserted as the key's initial value before it is incremented. The default value is 0.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.incr(:a)
{:ok, 1}
iex> MyCache.incr(:a, 2)
{:ok, 3}
iex> MyCache.incr(:a, -1)
{:ok, 2}
iex> MyCache.incr(:missing_key, 2, default: 10)
{:ok, 12}

# Initialize the counter with a TTL
iex> MyCache.incr(:new_counter, 10, ttl: :timer.seconds(1))
{:ok, 10}

incr(dynamic_cache, key, amount, opts)

@callback incr(dynamic_cache(), key(), amount :: integer(), opts()) ::
  ok_error_tuple(integer())

Same as incr/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.incr(MyCache1, :a, 1, [])
{:ok, 1}

incr!(key, amount, opts)

@callback incr!(key(), amount :: integer(), opts()) :: integer()

Same as incr/3 but raises an exception if an error occurs.

Examples

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

incr!(dynamic_cache, key, amount, opts)

@callback incr!(dynamic_cache(), key(), amount :: integer(), opts()) :: integer()

Same as incr!/4 but raises an exception if an error occurs.

put(key, value, opts)

@callback put(key(), value(), opts()) :: :ok | error_tuple()

Puts the given value under key into the cache.

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

Returns :ok if successful; {:error, reason} otherwise.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :keep_ttl (boolean/0) - Indicates whether to retain the time to live associated with the key. Otherwise, the value in the :ttl option overwrites the existing one. The default value is false.

See the "Shared options" section in the module documentation for more options.

Examples

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

Putting entries with specific time-to-live:

iex> MyCache.put("foo", "bar", ttl: :timer.seconds(10))
:ok
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(30))
:ok

To keep the current TTL in case the key already exists:

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

put(dynamic_cache, key, value, opts)

@callback put(dynamic_cache(), key(), value(), opts()) :: :ok | error_tuple()

Same as put/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

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

put!(key, value, opts)

@callback put!(key(), value(), opts()) :: :ok

Same as put/3 but raises an exception if an error occurs.

put!(dynamic_cache, key, value, opts)

@callback put!(dynamic_cache(), key(), value(), opts()) :: :ok

Same as put!/4 but raises an exception if an error occurs.

put_all(entries, opts)

@callback put_all(entries(), opts()) :: :ok | error_tuple()

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

Returns :ok if successful; {:error, reason} otherwise.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put_all(apples: 3, bananas: 1)
:ok
iex> MyCache.put_all(%{apples: 2, oranges: 1}, ttl: :timer.hours(1))
:ok

Atomic operation

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, reviewing the adapter's documentation is highly recommended.

put_all(dynamic_cache, entries, opts)

@callback put_all(dynamic_cache(), entries(), opts()) :: :ok | error_tuple()

Same as put_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put_all(MyCache1, [apples: 3, bananas: 1], [])
:ok
iex> MyCache.put_all(MyCache1, %{oranges: 1}, ttl: :timer.hours(1))
:ok

put_all!(entries, opts)

@callback put_all!(entries(), opts()) :: :ok

Same as put_all/2 but raises an exception if an error occurs.

put_all!(dynamic_cache, entries, opts)

@callback put_all!(dynamic_cache(), entries(), opts()) :: :ok

Same as put_all!/3 but raises an exception if an error occurs.

put_new(key, value, opts)

@callback put_new(key(), value(), opts()) :: ok_error_tuple(boolean())

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

Returns {:ok, true} if the value is stored; otherwise, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put_new("foo", "bar")
{:ok, true}
iex> MyCache.put_new("foo", "bar", ttl: :timer.hours(1))
{:ok, false}

put_new(dynamic_cache, key, value, opts)

@callback put_new(dynamic_cache(), key(), value(), opts()) :: ok_error_tuple(boolean())

Same as put_new/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put_new(MyCache1, "foo", "bar", [])
{:ok, true}
iex> MyCache.put_new(MyCache1, "foo", "bar", ttl: :timer.hours(1))
{:ok, false}

put_new!(key, value, opts)

@callback put_new!(key(), value(), opts()) :: boolean()

Same as put_new/3 but raises an exception if an error occurs.

Examples

iex> MyCache.put_new!("foo", "bar")
true
iex> MyCache.put_new!("foo", "bar", ttl: :timer.hours(1))
false

put_new!(dynamic_cache, key, value, opts)

@callback put_new!(dynamic_cache(), key(), value(), opts()) :: boolean()

Same as put_new!/4 but raises an exception if an error occurs.

put_new_all(entries, opts)

@callback put_new_all(entries(), opts()) :: ok_error_tuple(boolean())

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

Returns {:ok, true} if all entries are successfully stored, or {:ok, false} if no key was set (at least one key already existed).

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put_new_all(apples: 3, bananas: 1)
{:ok, true}
iex> MyCache.put_new_all(%{apples: 3, oranges: 1}, ttl: :timer.hours(1))
{:ok, false}

Atomic operation

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, reviewing the adapter's documentation is highly recommended.

put_new_all(dynamic_cache, entries, opts)

@callback put_new_all(dynamic_cache(), entries(), opts()) :: ok_error_tuple(boolean())

Same as put_new_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put_new_all(MyCache1, [apples: 3, bananas: 1], [])
{:ok, true}
iex> MyCache.put_new_all(MyCache1, %{apples: 3, oranges: 1}, ttl: :timer.seconds(10))
{:ok, false}

put_new_all!(entries, opts)

@callback put_new_all!(entries(), opts()) :: boolean()

Same as put_new_all/2 but raises an exception if an error occurs.

Examples

iex> MyCache.put_new_all!(apples: 3, bananas: 1)
true
iex> MyCache.put_new_all!(%{apples: 3, oranges: 1}, ttl: :timer.hours(1))
false

put_new_all!(dynamic_cache, entries, opts)

@callback put_new_all!(dynamic_cache(), entries(), opts()) :: boolean()

Same as put_new_all!/3 but raises an exception if an error occurs.

replace(key, value, opts)

@callback replace(key(), value(), opts()) :: ok_error_tuple(boolean())

Alters the entry stored under key, but only if the entry already exists in the cache.

Returns {:ok, true} if the value is replaced. Otherwise, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :keep_ttl (boolean/0) - Indicates whether to retain the time to live associated with the key. Otherwise, the value in the :ttl option overwrites the existing one. The default value is true.

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.replace("foo", "bar")
{:ok, false}
iex> MyCache.put_new("foo", "bar")
{:ok, true}
iex> MyCache.replace("foo", "bar2")
{:ok, true}

Update current value and TTL:

iex> MyCache.replace("foo", "bar3", ttl: :timer.seconds(10))
{:ok, true}

To keep the current TTL:

iex> MyCache.replace("foo", "bar4", keep_ttl: true)
{:ok, true}

replace(dynamic_cache, key, value, opts)

@callback replace(dynamic_cache(), key(), value(), opts()) :: ok_error_tuple(boolean())

Same as replace/3, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.replace(MyCache1, "foo", "bar", [])
{:ok, false}
iex> MyCache.put_new("foo", "bar")
{:ok, true}
iex> MyCache.replace(MyCache1, "foo", "bar", ttl: :timer.hours(1))
{:ok, true}

replace!(key, value, opts)

@callback replace!(key(), value(), opts()) :: boolean()

Same as replace/3 but raises an exception if an error occurs.

Examples

iex> MyCache.replace!("foo", "bar")
false
iex> MyCache.put_new!("foo", "bar")
true
iex> MyCache.replace!("foo", "bar2")
true

replace!(dynamic_cache, key, value, opts)

@callback replace!(dynamic_cache(), key(), value(), opts()) :: boolean()

Same as replace!/4 but raises an exception if an error occurs.

take(key, opts)

@callback take(key(), opts()) :: ok_error_tuple(value(), fetch_error_reason())

Removes and returns the value associated with key in the cache.

If key is present in the cache, its value is removed and returned as {:ok, value}.

If there's an error with executing the command, {:error, reason} is returned. reason is the cause of the error and can be Nebulex.KeyError if the cache does not contain key or Nebulex.Error otherwise.

Options

See the "Shared options" section in the module documentation for more options.

Examples

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

iex> {:error, %Nebulex.KeyError{key: :a} = e} = MyCache.take(:a)
iex> e.reason
:not_found

take(dynamic_cache, key, opts)

@callback take(dynamic_cache(), key(), opts()) ::
  ok_error_tuple(value(), fetch_error_reason())

Same as take/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.take(MyCache1, :a, [])
{:ok, 1}

take!(key, opts)

@callback take!(key(), opts()) :: value()

Same as take/2 but raises an exception if an error occurs.

Examples

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

take!(dynamic_cache, key, opts)

@callback take!(dynamic_cache(), key(), opts()) :: value()

Same as take!/3 but raises an exception if an error occurs.

touch(key, opts)

@callback touch(key(), opts()) :: ok_error_tuple(boolean())

Returns {:ok, true} if the given key exists and the last access time is successfully updated; otherwise, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put(:a, 1)
:ok
iex> MyCache.touch(:a)
{:ok, true}
iex> MyCache.ttl(:b)
{:ok, false}

touch(dynamic_cache, key, opts)

@callback touch(dynamic_cache(), key(), opts()) :: ok_error_tuple(boolean())

Same as touch/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.touch(MyCache1, :a, [])
{:ok, false}

touch!(key, opts)

@callback touch!(key(), opts()) :: boolean()

Same as touch/2 but raises an exception if an error occurs.

Examples

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

touch!(dynamic_cache, key, opts)

@callback touch!(dynamic_cache(), key(), opts()) :: boolean()

Same as touch!/3 but raises an exception if an error occurs.

ttl(key, opts)

@callback ttl(key(), opts()) :: ok_error_tuple(timeout(), fetch_error_reason())

Returns the remaining time-to-live for the given key.

If key is present in the cache, its remaining TTL is returned as {:ok, ttl}.

If there's an error with executing the command, {:error, reason} is returned. reason is the cause of the error and can be Nebulex.KeyError if the cache does not contain key, Nebulex.Error otherwise.

Options

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyCache.put(:a, 1, ttl: :timer.seconds(5))
:ok
iex> MyCache.put(:b, 2)
:ok
iex> MyCache.ttl(:a)
{:ok, _remaining_ttl}
iex> MyCache.ttl(:b)
{:ok, :infinity}

iex> {:error, %Nebulex.KeyError{key: :c} = e} = MyCache.ttl(:c)
iex> e.reason
:not_found

ttl(dynamic_cache, key, opts)

Same as ttl/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.put(:a, 1, ttl: :timer.seconds(5))
:ok
iex> MyCache.ttl(MyCache1, :a, [])
{:ok, _remaining_ttl}

ttl!(key, opts)

@callback ttl!(key(), opts()) :: timeout()

Same as ttl/2 but raises an exception if an error occurs.

Examples

iex> MyCache.put(:a, 1, ttl: :timer.seconds(5))
:ok
iex> MyCache.ttl!(:a)
_remaining_ttl

ttl!(dynamic_cache, key, opts)

@callback ttl!(dynamic_cache(), key(), opts()) :: timeout()

Same as ttl!/3 but raises an exception if an error occurs.

update(key, initial, function, opts)

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

Updates the key in the cache with the given function.

If key is present in the cache, the existing value is passed to fun and its result is used as the updated value of key. If key is not present in the cache, default is inserted as the value of key. The default value will not be passed through the update function.

This function returns:

  • {:ok, value} - The value associated with the key is updated.

  • {:error, reason} - An error occurred executing the command. reason is the cause.

Options

  • :ttl (timeout/0) - The key's time-to-live (or expiry time) in milliseconds (:infinity to store indefinitely). The default value is :infinity.

  • :keep_ttl (boolean/0) - Indicates whether to retain the time to live associated with the key. Otherwise, the value in the :ttl option overwrites the existing one. The default value is false.

See the "Shared options" section in the module documentation for more options.

Examples

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

update(dynamic_cache, key, initial, function, opts)

@callback update(dynamic_cache(), key(), initial :: value(), (value() -> value()), opts()) ::
  ok_error_tuple(value())

Same as update/4, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.update(MyCache1, :a, 1, &(&1 * 2), [])
{:ok, 1}

update!(key, initial, function, opts)

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

Same as update/4 but raises an exception if an error occurs.

Examples

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

update!(dynamic_cache, key, initial, function, opts)

@callback update!(
  dynamic_cache(),
  key(),
  initial :: value(),
  (value() -> value()),
  opts()
) :: value()

Same as update!/5 but raises an exception if an error occurs.

Query API

count_all(query_spec, opts)

(optional)
@callback count_all(query_spec(), opts()) :: ok_error_tuple(non_neg_integer())

Counts all entries matching the query specified by the given query_spec.

See get_all/2 for more information about the query_spec.

This function returns:

  • {:ok, count} - The cache executes the query successfully and returns the count of the matched entries.

  • {:error, reason} - An error occurred executing the command. reason is the cause.

May raise Nebulex.QueryError if query validation fails.

Options

See the "Shared options" section in the module documentation for more options.

Examples

Populate the cache with some entries:

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

Count all entries in cache (cache size):

iex> MyCache.count_all()
{:ok, 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> {:ok, count} = MyCache.count_all(query: query)

See get_all/2 for more query examples.

count_all(dynamic_cache, query_spec, opts)

(optional)
@callback count_all(dynamic_cache(), query_spec(), opts()) ::
  ok_error_tuple(non_neg_integer())

Same as count_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.count_all(MyCache1, [], [])
{:ok, 0}

count_all!(query_spec, opts)

(optional)
@callback count_all!(query_spec(), opts()) :: non_neg_integer()

Same as count_all/2 but raises an exception if an error occurs.

Examples

iex> MyCache.count_all!()
0

count_all!(dynamic_cache, query_spec, opts)

(optional)
@callback count_all!(dynamic_cache(), query_spec(), opts()) :: non_neg_integer()

Same as count_all!/3 but raises an exception if an error occurs.

delete_all(query_spec, opts)

(optional)
@callback delete_all(query_spec(), opts()) :: ok_error_tuple(non_neg_integer())

Deletes all entries matching the query specified by the given query_spec.

See get_all/2 for more information about the query_spec.

This function returns:

  • {:ok, deleted_count} - The cache executes the query successfully and returns the deleted entries count.

  • {:error, reason} - An error occurred executing the command. reason is the cause.

May raise Nebulex.QueryError if query validation fails.

Options

See the "Shared options" section in the module documentation for more options.

Examples

Populate the cache with some entries:

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

Delete all (default args):

iex> MyCache.delete_all()
{:ok, 5}

Delete only the requested keys (bulk delete):

iex> MyCache.delete_all(in: [1, 2, 10])
{:ok, 2}

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> {:ok, deleted_count} = MyCache.delete_all(query: query)

See get_all/2 for more query examples.

delete_all(dynamic_cache, query_spec, opts)

(optional)
@callback delete_all(dynamic_cache(), query_spec(), opts()) ::
  ok_error_tuple(non_neg_integer())

Same as delete_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.delete_all(MyCache1, [], [])
{:ok, 0}

delete_all!(query_spec, opts)

(optional)
@callback delete_all!(query_spec(), opts()) :: integer()

Same as delete_all/2 but raises an exception if an error occurs.

Examples

iex> MyCache.delete_all!()
0

delete_all!(dynamic_cache, query_spec, opts)

(optional)
@callback delete_all!(dynamic_cache(), query_spec(), opts()) :: integer()

Same as delete_all!/3 but raises an exception if an error occurs.

get_all(query_spec, opts)

(optional)
@callback get_all(query_spec(), opts()) :: ok_error_tuple([any()])

Fetches all entries from the cache matching the given query specified through the "query-spec".

This function returns:

  • {:ok, result} - The cache executes the query successfully. The result is a list with the matched entries.

  • {:error, reason} - An error occurred executing the command. reason is the cause.

May raise Nebulex.QueryError if query validation fails.

Query specification

There are two ways to use the Query API:

  • Fetch multiple keys (all at once), like a bulk fetch.
  • Fetch all entries from the cache matching a given query, more like a search (this is the most generic option).

Here is where the query_spec argument comes in to specify the type of query to run.

The query_spec argument is a keyword/0 with options defining the desired query. The query_spec fields or options are:

  • :in (list of term/0) - The list of keys to fetch. The value to return depends on the :select option. The :in option is a predefined query meant to fetch multiple keys simultaneously.

    If present, it overrides the :query option and instructs the underlying adapter to match the entries associated with the set of keys requested. For every key that does not hold a value or does not exist, it is ignored and not added to the returned list.

  • :query (term/0) - The query specification to match entries in the cache.

    If present and set to nil, it matches all entries in the cache. The nil is a predefined value all adapters must support. Other than that, the value depends entirely on the adapter. The adapter is responsible for defining the query or matching specification. For example, the Nebulex.Adapters.Local adapter supports the "ETS Match Spec".

    The default value is nil.

  • :select - Selects which fields to choose from the entry.

    The possible values are:

    • {:key, :value} - (Default) Selects the key and the value from the entry. They are returned as a tuple {key, value}.
    • :key - Selects the key from the entry.
    • :value - Selects the value from the entry.
    • :entry - Selects the whole entry with its fields (use it carefully). The adapter defines the entry, the structure, and its fields. Therefore, Nebulex recommends checking the adapter's documentation to understand the entry's structure with its fields and to verify if the select option is supported.

    The default value is {:key, :value}.

Fetching multiple keys

While you can perform any query using the :query option (even fetching multiple keys), the option :in is preferable. For example:

MyCache.get_all(in: ["a", "list", "of", "keys"])

Fetching all entries matching a given query

As mentioned above, the option :query is the most generic way to match entries in a cache. This option allows users to write custom queries to be executed by the underlying adapter.

For matching all cached entries, you can skip the :query option or set it to nil instead (the default). For example:

MyCache.get_all() #=> Equivalent to MyCache.get_all(query: nil)

Using a custom query:

MyCache.get_all(query: query_supported_by_the_adapter)

Nebulex recommends to see the adapter documentation when using this option.

Options

See the "Shared options" section in the module documentation for more options.

Examples

Populate the cache with some entries:

iex> MyCache.put_all(a: 1, b: 2, c: 3)
:ok

Fetch all entries in the cache:

iex> MyCache.get_all()
{:ok, [a: 1, b: 2, c: 3]}

Fetch all entries returning only the keys:

iex> MyCache.get_all(select: :key)
{:ok, [:a, :b, :c]}

Fetch all entries returning only the values:

iex> MyCache.get_all(select: :value)
{:ok, [1, 2, 3]}

Fetch only the requested keys (bulk fetch):

iex> MyCache.get_all(in: [:a, :b, :d])
{:ok, [a: 1, b: 2]}

Fetch the requested keys returning only the keys or values:

iex> MyCache.get_all(in: [:a, :b, :d], select: :key)
{:ok, [:a, :b]}
iex> MyCache.get_all(in: [:a, :b, :d], select: :value)
{:ok, [1, 2]}

Query examples for Nebulex.Adapters.Local adapter

The Nebulex.Adapters.Local adapter supports "ETS Match Spec" as query values (in addition to nil or the option :in).

You must know the adapter's entry structure for match-spec queries, which is {:entry, key, value, touched, ttl}. For example, one may write the following query:

iex> match_spec = [
...>   {
...>     {:entry, :"$1", :"$2", :_, :_},
...>     [{:>, :"$2", 1}],
...>     [{{:"$1", :"$2"}}]
...>   }
...> ]
iex> MyCache.get_all(query: match_spec)
{:ok, [b: 1, c: 3]}

get_all(dynamic_cache, query_spec, opts)

(optional)
@callback get_all(dynamic_cache(), query_spec(), opts()) :: ok_error_tuple([any()])

Same as get_all/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> MyCache.get_all(MyCache1, [], [])
{:ok, _matched_entries}

get_all!(query_spec, opts)

(optional)
@callback get_all!(query_spec(), opts()) :: [any()]

Same as get_all/2 but raises an exception if an error occurs.

Examples

iex> MyCache.put_all(a: 1, b: 2, c: 3)
:ok
iex> MyCache.get_all!()
[a: 1, b: 2, c: 3]
iex> MyCache.get_all!(in: [:a, :b])
[a: 1, b: 2]

get_all!(dynamic_cache, query_spec, opts)

(optional)
@callback get_all!(dynamic_cache(), query_spec(), opts()) :: [any()]

Same as get_all!/3 but raises an exception if an error occurs.

stream(query_spec, opts)

(optional)
@callback stream(query_spec(), opts()) :: ok_error_tuple(Enum.t())

Similar to get_all/2, but returns a lazy enumerable that emits all entries matching the query specified by the given query_spec.

See get_all/2 for more information about the query_spec.

This function returns:

  • {:ok, stream} - It returns a stream of values.

  • {:error, reason} - An error occurred executing the command. reason is the cause.

May raise Nebulex.QueryError if query validation fails.

Options

  • :max_entries (pos_integer/0) - The number of entries to load from the cache as we stream The default value is 100.

See the "Shared options" section in the module documentation for more options.

Examples

Populate the cache with some entries:

iex> MyCache.put_all(a: 1, b: 2, c: 3)
:ok

Stream all (default args):

iex> {:ok, stream} = MyCache.stream()
iex> Enum.to_list(stream)
[a: 1, b: 2, c: 3]

Stream all entries returning only the keys (with :max_entries option):

iex> {:ok, stream} = MyCache.stream([select: :key], max_entries: 2)
iex> Enum.to_list(stream)
[:a, :b, :c]

Stream all entries returning only the values:

iex> {:ok, stream} = MyCache.stream(select: :value)
iex> Enum.to_list(stream)
[1, 2, 3]

Stream only the requested keys (lazy bulk-fetch):

iex> {:ok, stream} = MyCache.stream(in: [:a, :b, :d])
iex> Enum.to_list(stream)
[a: 1, b: 2]
iex> {:ok, stream} = MyCache.stream(in: [:a, :b, :d], select: :key)
iex> Enum.to_list(stream)
[:a, :b]

stream(dynamic_cache, query_spec, opts)

(optional)
@callback stream(dynamic_cache(), query_spec(), opts()) :: ok_error_tuple(Enum.t())

Same as stream/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

iex> = MyCache.stream(MyCache1, [], [])
{:ok, _stream}

stream!(query_spec, opts)

(optional)
@callback stream!(query_spec(), opts()) :: Enum.t()

Same as stream/2 but raises an exception if an error occurs.

Examples

iex> MyCache.put_all(a: 1, b: 2, c: 3)
:ok
iex> MyCache.stream!() |> Enum.to_list()
[a: 1, b: 2, c: 3]

stream!(dynamic_cache, query_spec, opts)

(optional)
@callback stream!(dynamic_cache(), query_spec(), opts()) :: Enum.t()

Same as stream!/3 but raises an exception if an error occurs.

Transaction API

in_transaction?(opts)

(optional)
@callback in_transaction?(opts()) :: ok_error_tuple(boolean())

Returns {:ok, true} if the current process is inside a transaction; otherwise, {:ok, false} is returned.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

Options

See the "Shared options" section in the module documentation for more options.

Examples

MyCache.in_transaction?()
#=> {:ok, false}

MyCache.transaction(fn ->
  MyCache.in_transaction? #=> {:ok, true}
end)

in_transaction?(dynamic_cache, opts)

(optional)
@callback in_transaction?(dynamic_cache(), opts()) :: ok_error_tuple(boolean())

Same as in_transaction?/1, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

MyCache.in_transaction?(MyCache1, [])

transaction(fun, opts)

(optional)
@callback transaction(fun(), opts()) :: ok_error_tuple(any())

Runs the given function inside a transaction.

If an Elixir exception occurs, the exception will bubble up from the transaction function. If the cache aborts the transaction, it returns {:error, reason}.

A successful transaction returns the value returned by the function wrapped in a tuple as {:ok, value}.

Nested transactions

If transaction/2 is called inside another transaction, the cache executes the function without wrapping the new transaction call in any way.

Options

See the "Shared options" section in the module documentation for more 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)

We can provide the keys to lock when using the Nebulex.Adapters.Local adapter: (recommended):

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,
  [keys: [:alice, :bob]]
)

transaction(dynamic_cache, fun, opts)

(optional)
@callback transaction(dynamic_cache(), fun(), opts()) :: ok_error_tuple(any())

Same as transaction/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

MyCache.transaction(
  MyCache1,
  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,
  [keys: [:alice, :bob]]
)

Info API

info(spec, opts)

(optional)
@callback info(spec :: info_spec(), opts()) :: ok_error_tuple(info_data())

Returns {:ok, info} where info contains the requested cache information, as specified by the spec.

If there's an error with executing the command, {:error, reason} is returned, where reason is the cause of the error.

The spec (information specification key) can be:

  • The atom :all - returns a map with all information items.
  • An atom - returns the value for the requested information item.
  • A list of atoms - returns a map only with the requested information items.

If the argument spec is omitted, all information items are returned; same as if the spec was the atom :all.

The adapters are free to add the information specification keys they want. However, Nebulex suggests the adapters add the following keys:

  • :server - General information about the cache server (e.g., cache name, adapter, PID, etc.).
  • :memory - Memory consumption information (e.g., used memory, allocated memory, etc.).
  • :stats - Cache statistics (e.g., hits, misses, etc.).

Examples

The following examples assume the underlying adapter uses the implementation provided by Nebulex.Adapters.Common.Info.

iex> {:ok, info} = MyCache.info()
iex> info
%{
  server: %{
    nbx_version: "3.0.0",
    cache_module: "MyCache",
    cache_adapter: "Nebulex.Adapters.Local",
    cache_name: "MyCache",
    cache_pid: #PID<0.111.0>
  },
  memory: %{
    total: 1_000_000,
    used: 0
  },
  stats: %{
    deletions: 0,
    evictions: 0,
    expirations: 0,
    hits: 0,
    misses: 0,
    updates: 0,
    writes: 0
  }
}

iex> {:ok, info} = MyCache.info(:server)
iex> info
%{
  nbx_version: "3.0.0",
  cache_module: "MyCache",
  cache_adapter: "Nebulex.Adapters.Local",
  cache_name: "MyCache",
  cache_pid: #PID<0.111.0>
}

iex> {:ok, info} = MyCache.info([:server, :stats])
iex> info
%{
  server: %{
    nbx_version: "3.0.0",
    cache_module: "MyCache",
    cache_adapter: "Nebulex.Adapters.Local",
    cache_name: "MyCache",
    cache_pid: #PID<0.111.0>
  },
  stats: %{
    deletions: 0,
    evictions: 0,
    expirations: 0,
    hits: 0,
    misses: 0,
    updates: 0,
    writes: 0
  }
}

info(dynamic_cache, spec, opts)

(optional)
@callback info(dynamic_cache(), spec :: info_spec(), opts()) ::
  ok_error_tuple(info_data())

Same as info/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

MyCache.info(MyCache1, :all, [])

info!(spec, opts)

(optional)
@callback info!(spec :: info_spec(), opts()) :: info_data()

Same as info/2 but raises an exception if an error occurs.

info!(dynamic_cache, spec, opts)

(optional)
@callback info!(dynamic_cache(), spec :: info_spec(), opts()) :: info_data()

Same as info/3 but raises an exception if an error occurs.

Observable API

register_event_listener(event_listener, opts)

(optional)
@callback register_event_listener(event_listener(), opts()) :: :ok | error_tuple()

Register a cache event listener event_listener.

Returns :ok if successful; {:error, reason} otherwise.

Listeners should be implemented with care. In particular, it is important to consider their impact on performance and latency.

Listeners:

  • are fired after the entry is mutated in the cache.
  • block the calling process until the listener returns, when the listener is invoked synchronously; depends on the adapter's implementation.

Listeners follow the observer pattern. An exception raised by a listener does not cause the cache operation to fail.

Listeners can only raise Nebulex.Error exception. Caching implementations must catch any other exception from a listener, then wrap and reraise it as a Nebulex.Error exception.

Options

  • :id (term/0) - A unique identifier for the event listener. An error will be returned if another listener with the same ID already exists. Defaults to the event listener function itself.

  • :filter (Nebulex.Event.filter/0) - A function that may be used to check cache entry events prior to being dispatched to event listeners.

    A filter must not create side effects.

  • :metadata (Nebulex.Event.metadata/0) - The metadata is provided when registering the listener and added to the event at invoking the listener and filter functions; the event must always have a metadata field. The default value is [].

See the "Shared options" section in the module documentation for more options.

Examples

iex> MyApp.Cache.register_event_listener(&MyApp.handle/1)
:ok
iex> MyApp.Cache.register_event_listener(&MyApp.handle/1,
...>   filter: &MyApp.filter/1
...> )
:ok
iex> MyApp.Cache.register_event_listener(&MyApp.handle/2,
...>   filter: &MyApp.filter/2
...>   metadata: [foo: :bar]
...> )
:ok

# Register with `:id` (must be unregistered using the same `:id` value)
iex> MyApp.Cache.register_event_listener(&MyApp.handle/2,
...>   id: :my_listener,
...>   filter: &MyApp.filter/2
...>   metadata: [foo: :bar]
...> )
:ok

register_event_listener(dynamic_cache, event_listener, opts)

(optional)
@callback register_event_listener(dynamic_cache(), event_listener(), opts()) ::
  :ok | error_tuple()

Same as register_event_listener/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

MyApp.Cache.register_event_listener(:my_cache, &MyApp.handle/1, [])

register_event_listener!(event_listener, opts)

(optional)
@callback register_event_listener!(event_listener(), opts()) :: :ok

Same as register_event_listener/2 but raises an exception if an error occurs.

register_event_listener!(dynamic_cache, event_listener, opts)

(optional)
@callback register_event_listener!(dynamic_cache(), event_listener(), opts()) :: :ok

Same as register_event_listener/3 but raises an exception if an error occurs.

unregister_event_listener(id, opts)

(optional)
@callback unregister_event_listener(id :: any(), opts()) :: :ok | error_tuple()

Un-register a cache event listener.

Returns :ok if successful; {:error, reason} otherwise.

Options

See the "Shared options" section in the module documentation for more options.

Examples

# Register with default ID
iex> MyApp.Cache.register_event_listener(&MyApp.handle/1)
:ok
# Unregister with default ID
iex> MyApp.Cache.unregister_event_listener(&MyApp.handle/1)
:ok

# Register with `:id`
iex> MyApp.Cache.register_event_listener(&MyApp.handle/1, id: :listener)
:ok
# Unregister using the previously registered `:id`
iex> MyApp.Cache.unregister_event_listener(:listener)
:ok

unregister_event_listener(dynamic_cache, id, opts)

(optional)
@callback unregister_event_listener(dynamic_cache(), id :: any(), opts()) ::
  :ok | error_tuple()

Same as unregister_event_listener/2, but the command is executed on the cache instance given at the first argument dynamic_cache.

See the "Dynamic caches" section in the module documentation for more information.

Examples

MyApp.Cache.unregister_event_listener(:my_cache, &MyApp.handle/1, [])

unregister_event_listener!(id, opts)

(optional)
@callback unregister_event_listener!(id :: any(), opts()) :: :ok

Same as unregister_event_listener/2 but raises an exception if an error occurs.

unregister_event_listener!(dynamic_cache, id, opts)

(optional)
@callback unregister_event_listener!(dynamic_cache(), id :: any(), opts()) :: :ok

Same as unregister_event_listener/3 but raises an exception if an error occurs.

Types

dynamic_cache()

@type dynamic_cache() :: atom() | pid()

Dynamic cache value

entries()

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

Cache entries

error_tuple()

@type error_tuple() :: error_tuple(nbx_error_reason())

Common error type

error_tuple(reason)

@type error_tuple(reason) :: {:error, reason}

Error type for the given reason

event_filter()

@type event_filter() :: Nebulex.Event.filter()

Proxy type to a cache event filter

event_listener()

@type event_listener() :: Nebulex.Event.listener()

Proxy type to a cache event listener

fetch_error_reason()

@type fetch_error_reason() :: Nebulex.KeyError.t() | nbx_error_reason()

Fetch error reason

info_data()

@type info_data() :: info_map() | info_item()

The data type for the cache information

info_item()

@type info_item() :: any()

The type for the info item's value

info_map()

@type info_map() :: %{optional(atom()) => any()}

Info map

info_spec()

@type info_spec() :: :all | atom() | [atom()]

Specification key for the item(s) to include in the returned info

key()

@type key() :: any()

Cache entry key

nbx_error_reason()

@type nbx_error_reason() :: Nebulex.Error.t()

Proxy type for generic Nebulex error

ok_error_tuple(ok)

@type ok_error_tuple(ok) :: ok_error_tuple(ok, nbx_error_reason())

Ok/Error tuple with default error reasons

ok_error_tuple(ok, error)

@type ok_error_tuple(ok, error) :: {:ok, ok} | {:error, error}

Ok/Error type

opts()

@type opts() :: keyword()

Cache action options

query_spec()

@type query_spec() :: keyword()

The data type for a query spec

t()

@type t() :: module()

Cache type

value()

@type value() :: any()

Cache entry value