RedisServerWrapper

Hex.pmDocs

Manage redis-server processes from Elixir -- single instances, clusters, and sentinel topologies with GenServer lifecycle management.

No Docker required. Just redis-server and redis-cli on your PATH.

Installation

Add redis_server_wrapper to your list of dependencies in mix.exs:

def deps do
  [
    {:redis_server_wrapper, "~> 0.1.0"}
  ]
end

Prerequisites

You need redis-server and redis-cli installed and available on your PATH.

# macOS
brew install redis

# Ubuntu/Debian
sudo apt-get install redis-server

# Verify
redis-server --version

Quick Start

Single Server

{:ok, server} = RedisServerWrapper.start_server(port: 6400, password: "secret")

RedisServerWrapper.Server.ping(server)
#=> true

RedisServerWrapper.Server.run(server, ["SET", "key", "value"])
#=> {:ok, "OK"}

RedisServerWrapper.Server.run(server, ["GET", "key"])
#=> {:ok, "value"}

RedisServerWrapper.Server.stop(server)

Cluster

{:ok, cluster} = RedisServerWrapper.start_cluster(masters: 3, base_port: 7100)

RedisServerWrapper.Cluster.healthy?(cluster)
#=> true

RedisServerWrapper.Cluster.node_addrs(cluster)
#=> ["127.0.0.1:7100", "127.0.0.1:7101", "127.0.0.1:7102"]

RedisServerWrapper.Cluster.stop(cluster)

Sentinel

{:ok, sentinel} = RedisServerWrapper.start_sentinel(
  master_port: 6390,
  replicas: 2,
  sentinels: 3
)

RedisServerWrapper.Sentinel.healthy?(sentinel)
#=> true

RedisServerWrapper.Sentinel.master_addr(sentinel)
#=> "127.0.0.1:6390"

RedisServerWrapper.Sentinel.stop(sentinel)

Persistent Instances (Manager)

The Manager tracks instances across IEx sessions using a JSON state file:

RedisServerWrapper.Manager.start_basic(name: "dev-redis", port: 6400)
#=> {:ok, %{name: "dev-redis", url: "redis://:password@127.0.0.1:6400", ...}}

RedisServerWrapper.Manager.list()
#=> [%{name: "dev-redis", type: :basic, ...}]

RedisServerWrapper.Manager.stop("dev-redis")

Configuration

All Redis configuration directives can be set via the Config struct options. Use the :extra option as an escape hatch for any directive not covered by the typed fields:

RedisServerWrapper.start_server(
  port: 6400,
  password: "secret",
  maxmemory: "256mb",
  maxmemory_policy: "allkeys-lru",
  extra: [
    {"notify-keyspace-events", "KEA"},
    {"hz", "100"}
  ]
)

Chaos / Fault Injection

The Chaos module provides fault injection primitives for testing resilience:

alias RedisServerWrapper.{Chaos, Cluster, Server}

{:ok, cluster} = RedisServerWrapper.start_cluster(
  masters: 3,
  replicas_per_master: 1,
  base_port: 7100
)

# Kill the master owning a key's hash slot
{:ok, killed} = Chaos.kill_master(cluster, "user:123")

# Kill by slot number
{:ok, killed} = Chaos.kill_master(cluster, 5000)

# Freeze a node (SIGSTOP) and resume later
{:ok, os_pid} = Chaos.freeze_node(server)
# ... test timeout/retry behavior ...
Chaos.resume_node(os_pid)

# Freeze with automatic resume after a duration
Chaos.pause_node(server, 5_000)

# Simulate a network partition
nodes = Cluster.nodes(cluster)
{active, isolated} = Enum.split(nodes, 2)
{:ok, frozen_pids} = Chaos.partition(cluster, [active, isolated])

# Recover all frozen nodes
Chaos.recover(frozen_pids)

# Other tools
Chaos.slow_down(server, 2_000)        # CLIENT PAUSE
Chaos.flushall(server)                 # wipe all data
Chaos.fill_memory(server, 10_000)      # fill with 1KB dummy keys
Chaos.trigger_save(server)             # force BGSAVE
Chaos.random_kill(cluster)             # kill a random node
Chaos.trigger_failover(replica)        # CLUSTER FAILOVER on a replica

Use Cases

License

Licensed under either of

at your option. See LICENSE for details.