Skip to content

Fast, pipelined, resilient Redis driver for Elixir. 🛍

License

Notifications You must be signed in to change notification settings

whatyouhide/redix

Repository files navigation

Redix

hex.pm badge Documentation badge CI Coverage Status

Fast, pipelined, resilient Redis client for Elixir.

DALL·E Golden Retriever

Redix is a Redis and Valkey client written in pure Elixir with focus on speed, correctness, and resiliency (that is, being able to automatically reconnect to Redis in case of network errors).

This README refers to the main branch of Redix, not the latest released version on Hex. Make sure to check the documentation for the version you're using.

Features

  • Idiomatic interface for sending commands to Redis
  • Pipelining
  • Resiliency (automatic reconnections)
  • Pub/Sub
  • SSL
  • Redis Sentinel

Installation

Add the :redix dependency to your mix.exs file. If you plan on connecting to a Redis server over SSL you may want to add the optional :castore dependency as well:

defp deps do
  [
    {:redix, "~> 1.1"},
    {:castore, ">= 0.0.0"}
  ]
end

Then, run mix deps.get in your shell to fetch the new dependencies.

Usage

Redix is simple: it doesn't wrap Redis commands with Elixir functions. It only provides functions to send any Redis command to the Redis server. A Redis command is expressed as a list of strings making up the command and its arguments.

Connections are started via start_link/0,1,2:

{:ok, conn} = Redix.start_link(host: "example.com", port: 5000)
{:ok, conn} = Redix.start_link("redis:https://localhost:6379/3", name: :redix)

Commands can be sent using Redix.command/2,3:

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

Pipelines are just lists of commands sent all at once to Redis for which Redis replies with a list of responses. They can be used in Redix via Redix.pipeline/2,3:

Redix.pipeline(conn, [["INCR", "foo"], ["INCR", "foo"], ["INCRBY", "foo", "2"]])
#=> {:ok, [1, 2, 4]}

Redix.command/2,3 and Redix.pipeline/2,3 always return {:ok, result} or {:error, reason}. If you want to access the result directly and raise in case there's an error, bang! variants are provided:

Redix.command!(conn, ["PING"])
#=> "PONG"

Redix.pipeline!(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
#=> ["OK", "foo"]

Resiliency

Redix is resilient against network errors. For example, if the connection to Redis drops, Redix will automatically try to reconnect periodically at a given "backoff" interval. Look at the documentation for the Redix module and at the "Reconnections" page in the documentation for more information on the available options and on the exact reconnection behaviour.

Redis Sentinel

Redix supports Redis Sentinel out of the box. You can specify a list of sentinels to connect to when starting a Redix (or Redix.PubSub) connection. Every time that connection will need to connect to a Redis server (the first time or after a disconnection), it will try to connect to one of the sentinels in order to ask that sentinel for the current primary or a replica.

sentinels = ["redis:https://sent1.example.com:26379", "redis:https://sent2.example.com:26379"]
{:ok, primary} = Redix.start_link(sentinel: [sentinels: sentinels, group: "main"])
Terminology

Redix doesn't support the use of the terms "master" and "slave" that are usually used with Redis Sentinel. I don't think those are good terms to use, period. Instead, Redix uses the terms "primary" and "replica". If you're interested in the discussions around this, this issue in the Redis repository might be interesting to you.

Pub/Sub

A Redix.PubSub process can be started via Redix.PubSub.start_link/2:

{:ok, pubsub} = Redix.PubSub.start_link()

Most communication with the Redix.PubSub process happens via Elixir messages (that simulate a Pub/Sub interaction with the pub/sub server).

{:ok, pubsub} = Redix.PubSub.start_link()

Redix.PubSub.subscribe(pubsub, "my_channel", self())
#=> {:ok, ref}

Confirmation of subscriptions is delivered as an Elixir message:

receive do
  {:redix_pubsub, ^pubsub, ^ref, :subscribed, %{channel: "my_channel"}} -> :ok
end

If someone publishes a message on a channel we're subscribed to:

receive do
  {:redix_pubsub, ^pubsub, ^ref, :message, %{channel: "my_channel", payload: "hello"}} ->
    IO.puts("Received a message!")
end

Using Redix in the Real World™

Redix is low-level, but it's still built to handle most things thrown at it. For many applications, you can avoid pooling with little to no impact on performance. Read the "Real world usage" page in the documentation for more information on this and pooling strategies that work better with Redix.

Contributing

To run the Redix test suite you will have to have Redis running locally. Redix requires a somewhat complex setup for running tests (because it needs a few instances running, for pub/sub and sentinel). For this reason, in this repository you'll find a docker-compose.yml file so that you can use Docker and docker compose to spin up all the necessary Redis instances with just one command. Make sure you have Docker installed and then just run:

docker compose up

Now, you're ready to run tests with the $ mix test command.

License

Redix is released under the MIT license. See the license file.