# `Redix.Cluster`
[🔗](https://github.com/whatyouhide/redix/blob/v1.5.3/lib/redix/cluster.ex#L1)

Main API for using [Redis Cluster](https://redis.io/docs/management/scaling/).

For a higher-level guide, see the [Cluster guide](cluster.html).

The design of this module follows the same low-level philosophy as the rest of Redix:
it builds on existing `Redix` connections (one per cluster node), uses ETS for fast slot
lookups, and mostly mirrors the `Redix` API.

## Usage

You can start connections to a Redis Cluster similarly to how you'd start a single
`Redix` connection, but specifying a *list of seed nodes* and a *name*:

    {:ok, cluster} = Redix.Cluster.start_link(
      name: :my_cluster,
      nodes: ["redis://localhost:7000"]
    )

    Redix.Cluster.command(:my_cluster, ["SET", "mykey", "myvalue"])
    #=> {:ok, "OK"}
    Redix.Cluster.command(:my_cluster, ["GET", "mykey"])
    #=> {:ok, "myvalue"}

### Pipelines

Pipelines that span multiple hash slots are transparently split across nodes,
executed in parallel, and reassembled in the original order:

    Redix.Cluster.pipeline(:my_cluster, [
      ["SET", "key1", "a"],
      ["SET", "key2", "b"],
      ["GET", "key1"],
      ["GET", "key2"]
    ])
    #=> {:ok, ["OK", "OK", "a", "b"]}

### Transactions

`MULTI`/`EXEC` transactions require all keys to be in the same hash slot.
Use [hash tags](https://redis.io/docs/reference/cluster-spec/#hash-tags) to
ensure this:

    Redix.Cluster.transaction_pipeline(:my_cluster, [
      ["SET", "{user:1}.name", "Alice"],
      ["SET", "{user:1}.email", "alice@example.com"]
    ])

### Limitations

  * Only database `0` is supported (Redis Cluster does not support `SELECT`).
  * Only primary nodes are used for reads (no replica reads).
  * Pub/Sub is not supported through the cluster interface (*yet*).
  * The `:noreply_*` functions are not supported in cluster mode.

## Telemetry

`Redix.Cluster` emits cluster-specific Telemetry events for topology changes
and redirections. See `Redix.Telemetry` for details.

# `endpoint`
*since 1.6.0* 

```elixir
@type endpoint() :: String.t() | [host: String.t(), port: :inet.port_number()]
```

A node endpoint.

It can be either a Redis URI string (such as `"redis://localhost:7000"`)
or a keyword list with `:host` and `:port` keys
(such as `[host: "localhost", port: 7000]`).

# `child_spec`
*since 1.6.0* 

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Returns a child spec for use in supervision trees.

## Examples

    children = [
      {Redix.Cluster, name: :my_cluster, nodes: ["redis://localhost:7000"]}
    ]

# `command`
*since 1.6.0* 

```elixir
@spec command(atom(), Redix.command(), keyword()) ::
  {:ok, Redix.Protocol.redis_value()}
  | {:error, atom() | Redix.Error.t() | Redix.ConnectionError.t()}
```

Issues a command on the Redis Cluster.

The command is routed to the correct node based on the key's hash slot.
If the command has no key (`PING`), it is sent to a random node.

Returns `{:ok, response}` or `{:error, reason}`.

## Options

  * `:timeout` - request timeout in milliseconds. Defaults to `5_000`.

## Examples

    Redix.Cluster.command(:my_cluster, ["SET", "mykey", "foo"])
    #=> {:ok, "OK"}

    Redix.Cluster.command(:my_cluster, ["GET", "mykey"])
    #=> {:ok, "foo"}

# `command!`
*since 1.6.0* 

```elixir
@spec command!(atom(), Redix.command(), keyword()) :: Redix.Protocol.redis_value()
```

Same as `command/3` but raises on errors.

# `pipeline`
*since 1.6.0* 

```elixir
@spec pipeline(atom(), [Redix.command()], keyword()) ::
  {:ok, [Redix.Protocol.redis_value()]}
  | {:error, atom() | Redix.Error.t() | Redix.ConnectionError.t()}
```

Issues a pipeline of commands on the Redis Cluster.

Commands are grouped by target node based on key hash slots, sent *in parallel*
to the respective nodes, and results are reassembled in the original order.

Returns `{:ok, results}` or `{:error, reason}`.

## Options

  * `:timeout` - request timeout in milliseconds. Defaults to `5_000`.

## Examples

    Redix.Cluster.pipeline(:my_cluster, [["SET", "a", "1"], ["SET", "b", "2"]])
    #=> {:ok, ["OK", "OK"]}

# `pipeline!`
*since 1.6.0* 

```elixir
@spec pipeline!(atom(), [Redix.command()], keyword()) :: [
  Redix.Protocol.redis_value()
]
```

Same as `pipeline/3` but raises on errors.

# `start_link`
*since 1.6.0* 

```elixir
@spec start_link(keyword()) :: Supervisor.on_start()
```

Starts a connection to a Redis Cluster.

## Options

These are cluster-specific options:

* `:name` (`t:atom/0`) - Required. An atom to register the cluster process under.
  All internal resources (ETS tables, Registry, connection supervisor) are named
  deterministically based on this name. You use this name to issue commands:

      Redix.Cluster.start_link(name: :my_cluster, nodes: [...])
      Redix.Cluster.command(:my_cluster, ["GET", "key"])

* `:nodes` (list of `t:endpoint/0`) - Required. A list of seed nodes to connect to. Only **one reachable node** is needed:
  the full cluster topology is discovered automatically via `CLUSTER SLOTS`.

* `:topology_refresh_interval` (`t:timeout/0`) - How often (in milliseconds) to refresh the cluster topology. The default value is `30000`.

All other standard `Redix` connection options (`:password`, `:ssl`, `:socket_opts`,
`:timeout`, and so on) are passed through to *each underlying node connection*.

## Examples

    Redix.Cluster.start_link(
      name: :my_cluster,
      nodes: ["redis://localhost:7000", "redis://localhost:7001"]
    )

    Redix.Cluster.start_link(
      name: :my_cluster,
      nodes: [[host: "redis1.example.com", port: 6379]],
      password: "secret"
    )

# `stop`
*since 1.6.0* 

```elixir
@spec stop(atom(), timeout()) :: :ok
```

Stops the cluster and all its connections.

## Examples

    Redix.Cluster.stop(:my_cluster)

# `transaction_pipeline`
*since 1.6.0* 

```elixir
@spec transaction_pipeline(atom(), [Redix.command()], keyword()) ::
  {:ok, [Redix.Protocol.redis_value()]}
  | {:error, atom() | Redix.Error.t() | Redix.ConnectionError.t()}
```

Executes a `MULTI`/`EXEC` transaction on the Redis Cluster.

All commands must target the same hash slot (use hash tags to ensure this).
Returns `{:error, %Redix.Error{message: "CROSSSLOT" <> _}}` if commands
span multiple slots. At least one command must contain a key to route the
transaction on; a pipeline of only keyless commands (such as `PING`) returns
an error since there's no slot to send it to.

## Options

  * `:timeout` - request timeout in milliseconds. Defaults to `5_000`.

## Examples

    Redix.Cluster.transaction_pipeline(:my_cluster, [
      ["SET", "{user:1}.name", "Alice"],
      ["SET", "{user:1}.email", "alice@example.com"]
    ])
    #=> {:ok, ["OK", "OK"]}

# `transaction_pipeline!`
*since 1.6.0* 

```elixir
@spec transaction_pipeline!(atom(), [Redix.command()], keyword()) :: [
  Redix.Protocol.redis_value()
]
```

Same as `transaction_pipeline/3` but raises on errors.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
