View Source NebulexRedisAdapter (NebulexRedisAdapter v2.4.0)

Nebulex adapter for Redis. This adapter is implemented using Redix, a Redis driver for Elixir.

NebulexRedisAdapter provides three setup alternatives:

  • Standalone - The adapter establishes a pool of connections with a single Redis node. The :standalone is the default mode.

  • Redis Cluster - Redis Cluster is a built-in feature in Redis since version 3, and it may be the most convenient and recommendable way to set up Redis in a cluster and have a distributed cache storage out-of-box. This adapter provides the :redis_cluster mode to set up Redis Cluster from the client-side automatically and be able to use it transparently.

  • Built-in client-side cluster based on sharding - This adapter provides a simple client-side cluster implementation based on Sharding distribution model via :client_side_cluster mode.

Standalone

We can define a cache to use Redis as follows:

defmodule MyApp.RedisCache do
  use Nebulex.Cache,
    otp_app: :nebulex,
    adapter: NebulexRedisAdapter
end

The configuration for the cache must be in your application environment, usually defined in your config/config.exs:

config :my_app, MyApp.RedisCache,
  conn_opts: [
    host: "127.0.0.1",
    port: 6379
  ]

Redis Cluster

We can define a cache to use Redis Cluster as follows:

defmodule MyApp.RedisClusterCache do
  use Nebulex.Cache,
    otp_app: :nebulex,
    adapter: NebulexRedisAdapter
end

The config:

config :my_app, MyApp.RedisClusterCache,
  mode: :redis_cluster,
  redis_cluster: [
    configuration_endpoints: [
      endpoint1_conn_opts: [
        host: "127.0.0.1",
        port: 6379,
        # Add the password if 'requirepass' is on
        password: "password"
      ],
      ...
    ]
  ]

Client-side Cluster

We can define a cache with "client-side cluster mode" as follows:

defmodule MyApp.ClusteredCache do
  use Nebulex.Cache,
    otp_app: :nebulex,
    adapter: NebulexRedisAdapter
end

The config:

config :my_app, MyApp.ClusteredCache,
  mode: :client_side_cluster,
  client_side_cluster: [
    nodes: [
      node1: [
        pool_size: 10,
        conn_opts: [
          host: "127.0.0.1",
          port: 9001
        ]
      ],
      node2: [
        pool_size: 4,
        conn_opts: [
          url: "redis://127.0.0.1:9002"
        ]
      ],
      node3: [
        conn_opts: [
          host: "127.0.0.1",
          port: 9003
        ]
      ],
      ...
    ]
  ]

Configuration options

In addition to Nebulex.Cache config options, the adapter supports the following options:

  • :mode - Redis configuration mode.

    • :standalone - A single Redis instance. See the "Standalone" section in the module documentation for more options.
    • :redis_cluster - Redis Cluster setup. See the "Redis Cluster" section in the module documentation for more options.
    • :client_side_cluster - See the "Client-side Cluster" section in the module documentation for more options.

    The default value is :standalone.

  • :pool_size (pos_integer/0) - The number of connections that will be started by the adapter (based on the :mode). The default value is System.schedulers_online().

  • :serializer - Custom serializer module implementing the NebulexRedisAdapter.Serializer behaviour.

  • :serializer_opts (keyword/0) - Custom serializer options. The default value is [].

    • :encode_key (keyword/0) - Options for encoding the key. The default value is [].

    • :encode_value (keyword/0) - Options for encoding the value. The default value is [].

    • :decode_key (keyword/0) - Options for decoding the key. The default value is [].

    • :decode_value (keyword/0) - Options for decoding the value. The default value is [].

  • :conn_opts (keyword/0) - Redis client options. See Redix docs for more information about the options. The default value is [host: "127.0.0.1", port: 6379].

  • :redis_cluster - Required only when :mode is set to :redis_cluster. A keyword list of options.

    See "Redis Cluster options" section below.

  • :client_side_cluster - Required only when :mode is set to :client_side_cluster. A keyword list of options.

    See "Client-side Cluster options" section below.

Redis Cluster options

The available options are:

  • :configuration_endpoints - Required. A keyword list of named endpoints where the key is an atom as an identifier and the value is another keyword list of options (same as :conn_opts).

    See "Redis Cluster" for more information.

  • :override_master_host (boolean/0) - Defines whether the given master host should be overridden with the configuration endpoint or not. Defaults to false.

    The adapter uses the host returned by the "CLUSTER SHARDS" (Redis >= 7) or "CLUSTER SLOTS" (Redis < 7) command. One may consider set it to true for tests when using Docker for example, or when Redis nodes are behind a load balancer that Redis doesn't know the endpoint of. See Redis docs for more information.

    The default value is false.

  • :keyslot - The module implementing the Nebulex.Adapter.Keyslot behaviour, which is used to compute the node where the command will be applied to. The default value is NebulexRedisAdapter.RedisCluster.Keyslot.

Client-side Cluster options

The available options are:

Shared runtime options

Since the adapter runs on top of Redix, all commands accept their options (e.g.: :timeout, and :telemetry_metadata). See Redix docs for more information.

Redis Cluster runtime options

The following options apply to all commands:

  • :lock_retries - This option is specific to the :redis_cluster mode. When the config manager is running and setting up the hash slot map, all Redis commands get blocked until the cluster is properly configured and the hash slot map is ready to use. This option defines the max retry attempts to acquire the lock and execute the command in case the configuration manager is running and all commands are locked. Defaults to :infinity.

Custom Keyslot

As it is mentioned in the configuration options above, the :redis_cluster and :client_side_cluster modes have a default value for the :keyslot option. However, you can also provide your own implementation by implementing the Nebulex.Adapter.Keyslot and configuring the :keyslot option. For example:

defmodule MyApp.ClusteredCache.Keyslot do
  use Nebulex.Adapter.Keyslot

  @impl true
  def hash_slot(key, range) do
    # your implementation goes here
  end
end

And the config:

config :my_app, MyApp.ClusteredCache,
  mode: :client_side_cluster,
  client_side_cluster: [
    keyslot: MyApp.ClusteredCache.Keyslot,
    nodes: [
      ...
    ]
  ]

NOTE: For :redis_cluster mode, the:keyslot implementation must follow the Redis cluster specification.

TTL or Expiration Time

As is explained in Nebulex.Cache, most of the write-like functions support the :ttl option to define the expiration time, and it is defined in milliseconds. Despite Redis work with seconds, the conversion logic is handled by the adapter transparently, so when using a cache even with the Redis adapter, be sure you pass the :ttl option in milliseconds.

Data Types

Currently. the adapter only works with strings, which means a given Elixir term is encoded to a binary/string before executing a command. Similarly, a returned binary from Redis after executing a command is decoded into an Elixir term. The encoding/decoding process is performed by the adapter under-the-hood. However, it is possible to provide a custom serializer via the option :serializer. The value must be module implementing the NebulexRedisAdapter.Serializer behaviour.

NOTE: Support for other Redis Data Types is in the roadmap.

Queryable API

Since the queryable API is implemented by using KEYS command, keep in mind the following caveats:

  • Only keys can be queried.
  • Only strings and predefined queries are allowed as query values.

Predefined queries

  • nil - All keys are returned.

  • {:in, [term]} - Only the keys in the given key list ([term]) are returned. This predefined query is only supported for Nebulex.Cache.delete_all/2. This is the recommended way of doing bulk delete of keys.

Examples

iex> MyApp.RedisCache.put_all(%{
...>   "firstname" => "Albert",
...>   "lastname" => "Einstein",
...>   "age" => 76
...> })
:ok

iex> MyApp.RedisCache.all("**name**")
["firstname", "lastname"]

iex> MyApp.RedisCache.all("a??")
["age"]

iex> MyApp.RedisCache.all()
["age", "firstname", "lastname"]

iex> stream = MyApp.RedisCache.stream("**name**")
iex> stream |> Enum.to_list()
["firstname", "lastname"]

# get the values for the returned queried keys
iex> "**name**" |> MyApp.RedisCache.all() |> MyApp.RedisCache.get_all()
%{"firstname" => "Albert", "lastname" => "Einstein"}

Deleting multiple keys at once (bulk delete)

iex> MyApp.RedisCache.delete_all({:in, ["foo", "bar"]})
2

Transactions

This adapter doesn't provide support for transactions. However, in the future, it is planned support Redis Transactions by using the commands MULTI, EXEC, DISCARD and WATCH.

Running Redis commands and/or pipelines

Since NebulexRedisAdapter works on top of Redix and provides features like connection pools and "Redis Cluster" support, it may be seen also as a sort of Redis client, but it is meant to be used mainly with the Nebulex cache API. However, Redis API is quite extensive and there are a lot of useful commands we may want to run taking advantage of the NebulexRedisAdapter features. Therefore, the adapter provides two additional/extended functions to the defined cache: command!/2 and pipeline!/2.

command!(command, opts \\ [])

iex> MyCache.command!(["LPUSH", "mylist", "world"], key: "mylist")
1
iex> MyCache.command!(["LPUSH", "mylist", "hello"], key: "mylist")
2
iex> MyCache.command!(["LRANGE", "mylist", "0", "-1"], key: "mylist")
["hello", "world"]

pipeline!(commands, opts \\ [])

iex> [
...>   ["LPUSH", "mylist", "world"],
...>   ["LPUSH", "mylist", "hello"],
...>   ["LRANGE", "mylist", "0", "-1"]
...> ]
...> |> cache.pipeline!(key: "mylist")
[1, 2, ["hello", "world"]]

Options for command!/2 and pipeline!/2:

  • :key - It is required when used the adapter in mode :redis_cluster or :client_side_cluster so that the node where the commands will take place can be selected properly. For :standalone mode is not required (optional).
  • :name - The name of the cache in case you are using dynamic caches, otherwise it is not required.

Since these functions run on top of Redix, they also accept their options (e.g.: :timeout, and :telemetry_metadata). See Redix docs for more information.

Telemetry

This adapter emits the recommended Telemetry events. See the "Telemetry events" section in Nebulex.Cache for more information.

Adapter-specific telemetry events for the :redis_cluster mode

Aside from the recommended Telemetry events by Nebulex.Cache, this adapter exposes the following Telemetry events for the :redis_cluster mode:

  • telemetry_prefix ++ [:config_manager, :setup, :start] - This event is specific to the :redis_cluster mode. Before the configuration manager calls Redis to set up the cluster shards, this event should be invoked.

    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.
    • :pid - The configuration manager PID.
  • telemetry_prefix ++ [:config_manager, :setup, :stop] - This event is specific to the :redis_cluster mode. After the configuration manager set up the cluster shards, this event should be invoked.

    The :measurements map will include the following:

    • :duration - The time spent configuring the cluster. 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.
    • :pid - The configuration manager PID.
    • :status - The cluster setup status. If the cluster was configured successfully, the status will be set to :ok, otherwise, will be set to :error.
    • :error - The status reason. When the status is :ok, the reason is :succeeded, otherwise, it is the error reason.
  • telemetry_prefix ++ [:config_manager, :setup, :exception] - This event is specific to the :redis_cluster mode. When an exception is raised while configuring the cluster, this event should be invoked.

    The :measurements map will include the following:

    • :duration - The time spent configuring the cluster. 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.
    • :pid - The configuration manager PID.
    • :kind - The type of the error: :error, :exit, or :throw.
    • :reason - The reason of the error.
    • :stacktrace - The stacktrace.