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

This module provides the main API to interface with [Redis](http://redis.io) and
[Valkey](https://valkey.io/).

## Overview

`start_link/2` starts a process that connects to Redis. Each Elixir process
started with this function maps to a client TCP connection to the specified
Redis server.

The architecture is very simple: when you issue commands to Redis (via
`command/3` or `pipeline/3`), the Redix process sends the command to Redis right
away and is immediately able to send new commands. When a response arrives
from Redis, only then the Redix process replies to the caller with the
response. This pattern avoids blocking the Redix process for each request (until
a response arrives), increasing the performance of this driver.

## Reconnections

Redix tries to be as resilient as possible: it tries to recover automatically
from most network errors.

If there's a network error when sending data to Redis or if the connection to Redis
drops, Redix tries to reconnect. The first reconnection attempt will happen
after a fixed time interval; if this attempt fails, reconnections are
attempted until successful, and the time interval between reconnections is
increased exponentially. Some aspects of this behaviour can be configured; see
`start_link/2` and the "Reconnections" page in the docs for more information.

## Sentinel

Redix supports [Redis Sentinel](https://redis.io/topics/sentinel) by passing a `:sentinel`
option to `start_link/1` (or `start_link/2`) instead of `:host` and `:port`. In `:sentinel`,
you'll specify a list of sentinel nodes to try when connecting and the name of a primary group
(see `start_link/1` for more detailed information on these options). When connecting, Redix will
attempt to connect to each of the specified sentinels in the given order. When it manages to
connect to a sentinel, it will ask that sentinel for the address of the primary for the given
primary group. Then, it will connect to that primary and ask it for confirmation that it is
indeed a primary. If anything in this process doesn't go right, the next sentinel in the list
will be tried.

All of this happens in case of disconnections as well. If there's a disconnection, the whole
process of asking sentinels for a primary is executed again.

You should only care about Redis Sentinel when starting a `Redix` connection: once started,
using the connection will be exactly the same as the non-sentinel scenario.

## Transactions or pipelining?

Pipelining and transactions have things in common but they're fundamentally different.
With a pipeline, you're sending all commands in the pipeline *at once* on the connection
to Redis. This means Redis receives all commands at once, but the Redis server is not
guaranteed to process all those commands at once.

On the other hand, a `MULTI`/`EXEC` transaction guarantees that when `EXEC` is called
all the queued commands in the transaction are executed atomically. However, you don't
need to send all the commands in the transaction at once. If you want to combine
pipelining with `MULTI`/`EXEC` transactions, use `transaction_pipeline/3`.

## Skipping replies

Redis provides commands to control whether you want replies to your commands or not.
These commands are `CLIENT REPLY ON`, `CLIENT REPLY SKIP`, and `CLIENT REPLY OFF`.
When you use `CLIENT REPLY SKIP`, only the command that follows will not get a reply.
When you use `CLIENT REPLY OFF`, all the commands that follow will not get replies until
`CLIENT REPLY ON` is issued. Redix does not support these commands directly because they
would change the whole state of the connection. To skip replies, use `noreply_pipeline/3`
or `noreply_command/3`.

Skipping replies is useful to improve performance when you want to issue many commands
but are not interested in the responses to those commands.

> ### Blocked `CLIENT` commands {: .warning}
>
> Some servers may block `CLIENT` commands. For example, Google Memorystorage [does
> this](https://cloud.google.com/memorystore/docs/redis/product-constraints).
> If this is the case, the `noreply_*` functions mentioned above won't work.

## SSL

Redix supports SSL by passing `ssl: true` in `start_link/1`. You can use the `:socket_opts`
option to pass options that will be used by the SSL socket, like certificates.

If you are using Erlang/OTP 25+, Redix will use the system CA certificate store
automatically. If that is not available and the [CAStore](https://hex.pm/packages/castore)
dependency is available, Redix will use the CA certificate store file from CAStore
instead. You can select a different CA certificate store by passing in the `:cacertfile`
or `:cacerts` socket options. If the server uses a self-signed certificate, such as for
testing purposes, disable certificate verification by passing `verify: :verify_none` in
the socket options.

By default, Redix verifies the server hostname the same way browsers do (following
RFC 6125), which accepts the wildcard certificates used by servers such as Amazon
ElastiCache. This is done by setting `customize_hostname_check` to
`[match_fun: :public_key.pkix_verify_hostname_match_fun(:https)]`. You can override
this (or any other default SSL option) by passing your own value in `:socket_opts`:

    Redix.start_link(
      host: "example.com", port: 9999, ssl: true,
      socket_opts: [
        customize_hostname_check: [
          match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
        ]
      ]
    )

## Telemetry

Redix uses Telemetry for instrumentation and logging. See `Redix.Telemetry`.

# `command`

```elixir
@type command() :: [String.Chars.t()]
```

A command, which is a list of things that can be converted to strings.

For example, this is a valid command:

    ["INCR", :my_key, 1]

We recommend using strings directly to avoid needless conversions.

# `connection`

```elixir
@type connection() :: GenServer.server()
```

The reference to a Redix connection.

# `password`
*since 1.3.0* 

```elixir
@type password() ::
  String.t() | {module(), function_name :: atom(), arguments :: [term()]}
```

Passwords that can be passed to the `:password` option (see `start_link/1`).

# `sentinel_role`
*since 1.3.0* 

```elixir
@type sentinel_role() :: :primary | :replica
```

The possible role of a Redis sentinel (see `start_link/1`).

# `child_spec`

```elixir
@spec child_spec(uri | keyword() | {uri, keyword()}) :: Supervisor.child_spec()
when uri: binary()
```

Returns a child spec to use Redix in supervision trees.

To use Redix with the default options (same as calling `Redix.start_link()`):

    children = [
      Redix,
      # ...
    ]

You can pass options:

    children = [
      {Redix, host: "redix.example.com", name: :redix},
      # ...
    ]

You can also pass a URI:

    children = [
      {Redix, "redis://redix.example.com:6380"}
    ]

If you want to pass both a URI and options, you can do it by passing a tuple with the URI as the
first element and the list of options (make sure it has brackets around if using literals) as
the second element:

    children = [
      {Redix, {"redis://redix.example.com", [name: :redix]}}
    ]

# `command`

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

Issues a command on the Redis server.

This function sends `command` to the Redis server and returns the response
returned by Redis. `pid` must be the pid of a Redix connection. `command` must
be a list of strings making up the Redis command and its arguments.

The return value is `{:ok, response}` if the request is successful and the
response is not a Redis error. `{:error, reason}` is returned in case there's
an error in the request (such as losing the connection to Redis in between the
request). `reason` can also be a `Redix.Error` exception in case Redis is
reachable but returns an error (such as a type error).

If the given command is an empty command (`[]`), an `ArgumentError`
exception is raised.

This function accepts the same options as `pipeline/3`.

## Examples

    iex> Redix.command(conn, ["SET", "mykey", "foo"])
    {:ok, "OK"}
    iex> Redix.command(conn, ["GET", "mykey"])
    {:ok, "foo"}

    iex> Redix.command(conn, ["INCR", "mykey"])
    {:error, "ERR value is not an integer or out of range"}

If Redis goes down (before a reconnection happens):

    iex> {:error, error} = Redix.command(conn, ["GET", "mykey"])
    iex> error.reason
    :closed

# `command!`

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

Issues a command on the Redis server, raising if there's an error.

This function works exactly like `command/3` but:

  * if the command is successful, then the result is returned directly (not wrapped in a
    `{:ok, result}` tuple).
  * if there's a Redis error or a connection error, a `Redix.Error` or `Redix.ConnectionError`
    error is raised.

This function accepts the same options as `command/3`.

## Examples

    iex> Redix.command!(conn, ["SET", "mykey", "foo"])
    "OK"

    iex> Redix.command!(conn, ["INCR", "mykey"])
    ** (Redix.Error) ERR value is not an integer or out of range

If Redis goes down (before a reconnection happens):

    iex> Redix.command!(conn, ["GET", "mykey"])
    ** (Redix.ConnectionError) :closed

# `noreply_command`
*since 0.8.0* 

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

Same as `command/3` but tells the Redis server to not return a response.

This function is useful when you want to send a command but you don't care about the response.
Since the response is not returned, the return value of this function in case the command
is successfully sent to Redis is `:ok`.

Not receiving a response means saving traffic on the network and memory allocation for the
response. See also `noreply_pipeline/3`.

This function accepts the same options as `pipeline/3`.

## Examples

    iex> Redix.noreply_command(conn, ["INCR", "mykey"])
    :ok
    iex> Redix.command(conn, ["GET", "mykey"])
    {:ok, "1"}

# `noreply_command!`
*since 0.8.0* 

```elixir
@spec noreply_command!(connection(), command(), keyword()) :: :ok
```

Same as `noreply_command/3` but raises in case of errors.

# `noreply_pipeline`
*since 0.8.0* 

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

Issues a pipeline of commands to the Redis server, asking the server to not send responses.

This function is useful when you want to issue commands to the Redis server but you don't
care about the responses. For example, you might want to set a bunch of keys but you don't
care for a confirmation that they were set. In these cases, you can save bandwidth by asking
Redis to not send replies to your commands.

Since no replies are sent back, this function returns `:ok` in case there are no network
errors, or `{:error, reason}` otherwise.any()

This function accepts the same options as `pipeline/3`.

## Examples

    iex> commands = [["INCR", "mykey"], ["INCR", "meykey"]]
    iex> Redix.noreply_pipeline(conn, commands)
    :ok
    iex> Redix.command(conn, ["GET", "mykey"])
    {:ok, "2"}

# `noreply_pipeline!`
*since 0.8.0* 

```elixir
@spec noreply_pipeline!(connection(), [command()], keyword()) :: :ok
```

Same as `noreply_pipeline/3` but raises in case of errors.

# `pipeline`

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

Issues a pipeline of commands on the Redis server.

`commands` must be a list of commands, where each command is a list of strings
making up the command and its arguments. The commands will be sent as a single
"block" to Redis, and a list of ordered responses (one for each command) will
be returned.

The return value is `{:ok, results}` if the request is successful, `{:error,
reason}` otherwise.

Note that `{:ok, results}` is returned even if `results` contains one or more
Redis errors (`Redix.Error` structs). This is done to avoid having to walk the
list of results (a `O(n)` operation) to look for errors, leaving the
responsibility to the user. That said, errors other than Redis errors (like
network errors) always cause the return value to be `{:error, reason}`.

If `commands` is an empty list (`[]`) or any of the commands in `commands` is
an empty command (`[]`) then an `ArgumentError` exception is raised right
away.

Pipelining is not the same as a transaction. For more information, see the
module documentation.

## Options

* `:timeout` (`t:timeout/0`) - request timeout (in milliseconds). If the Redis server doesn't reply within this timeout,
  `{:error, %Redix.ConnectionError{reason: :timeout}}` is returned. The default value is `5000`.

* `:telemetry_metadata` (map of `t:term/0` keys and `t:term/0` values) - extra metadata to add to the `[:redix, :pipeline, *]` Telemetry events.
  These end up in the `:extra_metadata` metadata key of these events. See `Redix.Telemetry`. The default value is `%{}`.

## Examples

    iex> Redix.pipeline(conn, [["INCR", "mykey"], ["INCR", "mykey"], ["DECR", "mykey"]])
    {:ok, [1, 2, 1]}

    iex> Redix.pipeline(conn, [["SET", "k", "foo"], ["INCR", "k"], ["GET", "k"]])
    {:ok, ["OK", %Redix.Error{message: "ERR value is not an integer or out of range"}, "foo"]}

If Redis goes down (before a reconnection happens):

    iex> {:error, error} = Redix.pipeline(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
    iex> error.reason
    :closed

Extra Telemetry metadata:

    iex> Redix.pipeline(conn, [["PING"]], telemetry_metadata: %{connection: "My conn"})

# `pipeline!`

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

Issues a pipeline of commands to the Redis server, raising if there's an error.

This function works similarly to `pipeline/3`, except:

  * if there are no errors in issuing the commands (even if there are one or
    more Redis errors in the results), the results are returned directly (not
    wrapped in a `{:ok, results}` tuple).
  * if there's a connection error then a `Redix.ConnectionError` exception is raised.

For more information on why nothing is raised if there are one or more Redis
errors (`Redix.Error` structs) in the list of results, look at the
documentation for `pipeline/3`.

This function accepts the same options as `pipeline/3`.

## Examples

    iex> Redix.pipeline!(conn, [["INCR", "mykey"], ["INCR", "mykey"], ["DECR", "mykey"]])
    [1, 2, 1]

    iex> Redix.pipeline!(conn, [["SET", "k", "foo"], ["INCR", "k"], ["GET", "k"]])
    ["OK", %Redix.Error{message: "ERR value is not an integer or out of range"}, "foo"]

If Redis goes down (before a reconnection happens):

    iex> Redix.pipeline!(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
    ** (Redix.ConnectionError) :closed

# `start_link`

```elixir
@spec start_link(binary() | keyword()) :: {:ok, pid()} | :ignore | {:error, term()}
```

Starts a connection to Redis.

This function returns `{:ok, pid}` if the Redix process is started
successfully.

    {:ok, pid} = Redix.start_link()

The actual TCP connection to the Redis server may happen either synchronously,
before `start_link/2` returns, or asynchronously. This behaviour is decided by
the `:sync_connect` option (see below).

This function accepts one argument which can either be an string representing
a URI or a keyword list of options.

## Using in supervision trees

Redix supports child specs, so you can use it as part of a supervision tree:

    children = [
      {Redix, host: "redix.myapp.com", name: :redix}
    ]

See `child_spec/1` for more information.

## Using a Redis URI

In case `uri_or_options` is a Redis URI, it must be in the form:

    redis://[username:password@]host[:port][/db]

Here are some examples of valid URIs:

  * `redis://localhost`
  * `redis://:secret@localhost:6397`
  * `redis://username:secret@localhost:6397`
  * `redis://example.com:6380/1`
  * `rediss://example.com:6380/1` (for SSL connections)
  * `valkey://example.com:6380/1` (for [Valkey](https://valkey.io/) connections)

The only mandatory thing when using URIs is the host. All other elements are optional
and their default value can be found in the "Options" section below.

In earlier versions of Redix, the username in the URI was ignored. Redis 6 introduced [ACL
support](https://redis.io/topics/acl). Now, Redix supports usernames as well.

> #### Valkey {: .info}
>
> The `valkey://` schema is supported since Redix v1.5.0.

## Options

The following options can be used to specify the connection:

* `:host` (`t:String.t/0`) - the host where the Redis server is running. If you are using a Redis URI, you cannot
  use this option. Defaults to `"localhost`".

* `:port` (`t:non_neg_integer/0`) - the port on which the Redis server is running. If you are using a Redis URI, you cannot
  use this option. Defaults to `6379`.

* `:database` (`t:String.t/0` or `t:non_neg_integer/0`) - the database to connect to. Defaults to `nil`, meaning Redix doesn't connect to a
  specific database (the default in this case is database `0`). When this option is provided,
  all Redix does is issue a `SELECT` command to Redis in order to select the given database.

* `:username` - the username to connect to Redis. Defaults to `nil`, meaning no username is used.
  Redis supports usernames only since Redis 6 (see the [ACL
  documentation](https://redis.io/topics/acl)). If a username is provided (either via
  options or via URIs) and the Redis version used doesn't support ACL, then Redix falls
  back to using just the password and emits a warning. In future Redix versions, Redix
  will raise if a username is passed and the Redis version used doesn't support ACL.

* `:password` (`t:Redix.password/0`) - the password used to connect to Redis. Defaults to
  `nil`, meaning no password is used. When this option is provided, all Redix
  does is issue an `AUTH` command to Redis in order to authenticate. MFAs are also
  supported in the form of `{module, function, arguments}`. This can be used
  to fetch the password dynamically on every reconnection but most importantly to
  hide the password from crash reports in case the Redix connection crashes for
  any reason. For example, you can set this option to:
  `{System, :fetch_env!, ["REDIX_PASSWORD"]}`.

* `:timeout` (`t:timeout/0`) - connection timeout (in milliseconds) directly passed to the network layer. The default value is `5000`.

* `:health_check_interval` (`t:timeout/0`) - if set to a number of milliseconds, Redix periodically checks that in-flight commands
  are making progress. If a command stays in flight (sent to Redis but not yet answered)
  for longer than this interval, Redix considers the connection broken, even if the
  underlying socket is still open, then closes it and reconnects. This catches "half-open"
  connections where the server stops replying without closing the socket, which
  otherwise leave Redix hanging until eventually the socket goes down. The most
  common case is a Redis Sentinel failover: the old primary is paused (for example via
  `CLIENT PAUSE`) while a replica is promoted, so commands time out but the socket stays
  open. Reconnecting re-runs the connection logic, which for Sentinel means re-querying
  the sentinels for the *current* primary. Detection happens within one to two intervals.
  `:infinity` disables the check (the historical behavior). Blocking commands (such as
  `BLPOP`, `WAIT`, or `XREAD`/`XREADGROUP` with `BLOCK`) are handled automatically: while
  one of them is at the head of the in-flight queue the check is suspended, so it won't
  trip on a connection that is legitimately waiting, and it resumes as soon as the
  blocking command completes. *Available since v1.6.0.* The default value is `:infinity`.

* `:sync_connect` (`t:boolean/0`) - decides whether Redix should initiate the network connection to the Redis server *before*
  or *after* returning from `start_link/1`. This option also changes some reconnection
  semantics; read the "Reconnections" page in the documentation for more information. The default value is `false`.

* `:exit_on_disconnection` (`t:boolean/0`) - if `true`, the Redix server will exit if it fails to connect or disconnects from Redis.
  Note that setting this option to `true` means that the `:backoff_initial` and
  `:backoff_max` options will be ignored. The default value is `false`.

* `:backoff_initial` (`t:non_neg_integer/0`) - the initial backoff time (in milliseconds), which is the time that the Redix process
  will wait before attempting to reconnect to Redis after a disconnection or failed first
  connection. See the "Reconnections" page in the docs for more information. The default value is `500`.

* `:backoff_max` (`t:timeout/0`) - the maximum length (in milliseconds) of the time interval used between reconnection
  attempts. See the "Reconnections" page in the docs for more information. The default value is `30000`.

* `:ssl` (`t:boolean/0`) - if `true`, connect through SSL, otherwise through TCP. The `:socket_opts` option applies
  to both SSL and TCP, so it can be used for things like certificates. See `:ssl.connect/4`. The default value is `false`.

* `:name` (`t:term/0`) - Redix is bound to the same registration rules as a `GenServer`. See the `GenServer`
  documentation for more information.

* `:socket_opts` (list of `t:term/0`) - specifies a list of options that are passed to the network layer when connecting to
  the Redis server. Some socket options (like `:active` or `:binary`) will be
  overridden by Redix so that it functions properly.

  If `ssl: true`, then these are added to the default: `[verify: :verify_peer, depth: 3]`.
  If you are using Erlang/OTP 25+ or if the `CAStore` dependency is available, the
  `:cacerts` or `:cacertfile` option is added to the SSL options by default as well,
  unless either option is already specified.

  The default value is `[]`.

* `:hibernate_after` (`t:non_neg_integer/0`) - if present, the Redix connection process awaits any message for the given number
  of milliseconds and if no message is received, the process goes into hibernation
  automatically (by calling `:proc_lib.hibernate/3`). See `t::gen_statem.start_opt/0`.
  Not present by default.

* `:spawn_opt` (`t:keyword/0`) - if present, its value is passed as options to the Redix connection process as in
  `Process.spawn/4`. See `t::gen_statem.start_opt/0`. Not present by default.

* `:debug` (`t:keyword/0`) - if present, the corresponding function in the
  [`:sys` module](http://www.erlang.org/doc/man/sys.html) is invoked.

* `:readonly` (`t:boolean/0`) - if `true`, Redix issues a `READONLY` command right after connecting (and after every
  reconnection). This makes a connection to a Redis Cluster *replica* serve read-only
  commands instead of redirecting them to the primary. It has no effect on a primary.
  `Redix.Cluster` sets this automatically for replica connections when
  `:read_from_replicas` is enabled, so you rarely need to set it by hand.
  *Available since v1.6.0.* The default value is `false`.

* `:sentinel` (`t:keyword/0`) - options to use Redis Sentinel. If this option is present, you cannot use the `:host` and
  `:port` options. See the [*Sentinel Options* section below](#start_link/1-sentinel-options).

### Sentinel Options

* `:sentinels` (list of `t:String.t/0` or `t:keyword/0`) - Required. a list of sentinel addresses. Each element in this list is the address
  of a sentinel to be contacted in order to obtain the address of a primary. The address of
  a sentinel can be passed as a Redis URI (see the "Using a Redis URI" section) or
  a keyword list with `:host`, `:port`, `:password` options (same as when connecting to a
  Redis instance directly). Note that the password can either be passed in the sentinel
  address or globally — see the `:password` option below.

* `:group` (`t:String.t/0`) - Required. the name of the group that identifies the primary in the sentinel configuration.

* `:role` (`t:Redix.sentinel_role/0`) - if `:primary`, the connection will be established
  with the primary for the given group. If `:replica`, Redix will ask the sentinel for all
  the available replicas for the given group and try to connect to one of them
  **at random**. The default value is `:primary`.

* `:socket_opts` (`t:keyword/0`) - socket options for connecting to each sentinel. Same as the `:socket_opts` option
  described above. The default value is `[]`.

* `:timeout` (`t:timeout/0`) - the timeout (in milliseconds or `:infinity`) that will be used to
  interact with the sentinels. This timeout will be used as the timeout when connecting to
  each sentinel and when asking sentinels for a primary. The Redis documentation suggests
  to keep this timeout short so that connection to Redis can happen quickly. The default value is `500`.

* `:ssl` (`t:boolean/0`) - whether to use SSL to connect to each sentinel. The default value is `false`.

* `:password` (`t:Redix.password/0`) - if you don't want to specify a password for each sentinel you
  list, you can use this option to specify a password that will be used to authenticate
  on sentinels if they don't specify a password. This option is recommended over passing
  a password for each sentinel because in the future we might do sentinel auto-discovery,
  which means authentication can only be done through a global password that works for all
  sentinels.

## Examples

    iex> Redix.start_link()
    {:ok, #PID<...>}

    iex> Redix.start_link(host: "example.com", port: 9999, password: "secret")
    {:ok, #PID<...>}

    iex> Redix.start_link(database: 3, name: :redix_3)
    {:ok, #PID<...>}

# `start_link`

```elixir
@spec start_link(
  binary(),
  keyword()
) :: {:ok, pid()} | :ignore | {:error, term()}
```

Starts a connection to Redis.

This is the same as `start_link/1`, but the URI and the options get merged. `other_opts` have
precedence over the things specified in `uri`. Take this code:

    Redix.start_link("redis://localhost:6379", port: 6380)

In this example, port `6380` will be used.

# `stop`

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

Closes the connection to the Redis server.

This function is synchronous and blocks until the given Redix connection frees
all its resources and disconnects from the Redis server. `timeout` can be
passed to limit the amount of time allowed for the connection to exit; if it
doesn't exit in the given interval, this call exits.

## Examples

    iex> Redix.stop(conn)
    :ok

# `transaction_pipeline`
*since 0.8.0* 

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

Executes a `MULTI`/`EXEC` transaction.

Redis supports something akin to transactions. It works by sending a `MULTI` command,
then some commands, and then an `EXEC` command. All the commands after `MULTI` are
queued until `EXEC` is issued. When `EXEC` is issued, all the responses to the queued
commands are returned in a list.

This function accepts the same options as `pipeline/3`.

## Examples

To run a `MULTI`/`EXEC` transaction in one go, use this function and pass a list of
commands to use in the transaction:

    iex> Redix.transaction_pipeline(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
    {:ok, ["OK", "foo"]}

## Problems with transactions

There's an inherent problem with Redix's architecture and `MULTI`/`EXEC` transaction.
A Redix process is a single connection to Redis that can be used by many clients. If
a client A sends `MULTI` and client B sends a command before client A sends `EXEC`,
client B's command will be part of the transaction. This is intended behaviour, but
it might not be what you expect. This is why `transaction_pipeline/3` exists: this function
wraps `commands` in `MULTI`/`EXEC` but *sends all in a pipeline*. Since everything
is sent in the pipeline, it's sent at once on the connection and no commands can
end up in the middle of the transaction.

## Running `MULTI`/`EXEC` transactions manually

There are still some cases where you might want to start a transaction with `MULTI`,
then send commands from different processes that you actively want to be in the
transaction, and then send an `EXEC` to run the transaction. It's still fine to do
this with `command/3` or `pipeline/3`, but remember what explained in the section
above. If you do this, do it in an isolated connection (open a new one if necessary)
to avoid mixing things up.

# `transaction_pipeline!`
*since 0.8.0* 

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

Executes a `MULTI`/`EXEC` transaction.

Same as `transaction_pipeline/3`, but returns the result directly instead of wrapping it
in an `{:ok, result}` tuple or raises if there's an error.

This function accepts the same options as `pipeline/3`.

## Examples

    iex> Redix.transaction_pipeline!(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
    ["OK", "foo"]

---

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