rediscluster

package module
v0.0.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2026 License: MIT Imports: 11 Imported by: 0

README

testcontainers-go-redis-cluster

A testcontainers-go module for running a Redis cluster in tests.

All cluster nodes run as separate redis-server processes inside a single Docker container, so you get a real Redis cluster without the overhead of multiple containers.

Requirements

  • Go 1.25+
  • Docker

Installation

go get github.com/erkattak/testcontainers-go-redis-cluster

Usage

import (
    rediscluster "github.com/erkattak/testcontainers-go-redis-cluster"
    "github.com/redis/go-redis/v9"
    "github.com/testcontainers/testcontainers-go"
)

func TestSomething(t *testing.T) {
    cluster, err := rediscluster.Run(t.Context(), "redis:alpine")
    require.NoError(t, err)
    t.Cleanup(func() { testcontainers.CleanupContainer(t, cluster) })

    client := redis.NewClusterClient(&redis.ClusterOptions{
        Addrs:  cluster.Addrs(),
        Dialer: cluster.NewDialer(),
    })
    defer client.Close()

    err = client.Set(t.Context(), "key", "value", 0).Err()
    require.NoError(t, err)
}

Options

Option Default Description
WithMasters(n) 3 Number of master nodes (minimum 3)
WithReplicasPerMaster(n) 1 Number of replicas per master
WithPassword(p) "" AUTH password for all nodes
WithClusterNodeTimeout(d) 1s How long a node must be unreachable before failover starts
WithAppendOnly() disabled Enable AOF persistence
WithMaxMemory(limit) "" Max memory per node (e.g. "100mb")
WithMaxMemoryPolicy(policy) "" Eviction policy (e.g. "allkeys-lru")
WithLogLevel(level) "" Redis log level: debug, verbose, notice, warning
WithAllowReadsWhenDown() disabled Serve stale reads in degraded state
WithAllowWritesWhenDown() disabled Accept writes in degraded state (Redis 7+)
WithoutFullCoverage() disabled Serve covered slots even when some hash slots are unassigned
WithReplicaNoFailover() disabled Prevent automatic replica promotion

Standard testcontainers.ContainerCustomizer options (e.g. testcontainers.WithLogger) are also accepted and passed through to the container request.

Fault injection

StopNode and PauseNode let you simulate node failures in tests. Both return an idempotent undo function and support optional auto-recovery.

// Stop node 0; it will restart automatically after 3 seconds.
_, err := cluster.StopNode(ctx, 0, 3*time.Second)

// Pause node 0 (SIGSTOP); resume manually.
resumeFn, err := cluster.PauseNode(ctx, 0, 0)
// ... run assertions ...
err = resumeFn()

How it works

Single-container design

All Redis nodes run as separate processes inside one container. Ports start at 7000 and increment — a 3-master/1-replica cluster uses ports 7000–7005, all exposed and mapped to random host ports.

NAT-rewriting dialer

Redis cluster nodes announce their internal addresses to clients. Since clients connect from the host, those addresses are unreachable. NewDialer() returns a dialer that transparently rewrites internal containerIP:port and 127.0.0.1:port addresses to the corresponding host:mappedPort before dialing.

Always pass both Addrs() and Dialer: cluster.NewDialer() to your Redis client.

Documentation

Overview

Package rediscluster provides a testcontainers-go module for running a Redis cluster inside a single Docker container. All cluster nodes are started as separate redis-server processes within that container and a NAT-rewriting dialer is provided so that go-redis clients can connect from the host.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func WithAppendOnly

func WithAppendOnly() testcontainers.ContainerCustomizer

WithAppendOnly enables Redis AOF persistence (appendonly yes).

func WithClusterAllowReadsWhenDown added in v0.0.2

func WithClusterAllowReadsWhenDown() testcontainers.ContainerCustomizer

WithClusterAllowReadsWhenDown allows replica nodes to serve stale reads even when the cluster is in a degraded state. Useful for testing read behaviour during partial outages.

func WithClusterAllowWritesWhenDown added in v0.0.2

func WithClusterAllowWritesWhenDown() testcontainers.ContainerCustomizer

WithClusterAllowWritesWhenDown allows nodes to accept writes even when the cluster is in a degraded state. Requires Redis 7+.

func WithClusterMigrationBarrier added in v0.0.2

func WithClusterMigrationBarrier(n int) testcontainers.ContainerCustomizer

WithClusterMigrationBarrier sets the minimum number of replicas a master must retain before one of its replicas can be migrated to an orphaned master (cluster-migration-barrier). The Redis default is 1. Setting a higher value makes automatic replica migration less aggressive.

func WithClusterNodeTimeout

func WithClusterNodeTimeout(d time.Duration) testcontainers.ContainerCustomizer

WithClusterNodeTimeout sets how long a node must be unreachable before the cluster considers it failed and starts a failover. Defaults to 1s, which is appropriate for tests. Production clusters typically use 5–15s.

func WithClusterReplicaNoFailover added in v0.0.2

func WithClusterReplicaNoFailover() testcontainers.ContainerCustomizer

WithClusterReplicaNoFailover prevents replicas from automatically promoting to master when a master fails. Useful for fault-injection tests where failover should be triggered manually.

func WithClusterReplicaValidityFactor added in v0.0.2

func WithClusterReplicaValidityFactor(n int) testcontainers.ContainerCustomizer

WithClusterReplicaValidityFactor controls whether a replica that has been disconnected from its master for an extended period is still eligible to failover (cluster-replica-validity-factor). A value of 0 means replicas are always eligible regardless of disconnection time, which is the typical production setting for maximising availability.

func WithLogLevel

func WithLogLevel(level string) testcontainers.ContainerCustomizer

WithLogLevel sets the Redis log level: debug, verbose, notice, or warning.

func WithMasters

func WithMasters(n int) testcontainers.ContainerCustomizer

WithMasters sets the number of master nodes. Minimum 3 for a valid Redis cluster.

func WithMaxMemory

func WithMaxMemory(limit string) testcontainers.ContainerCustomizer

WithMaxMemory sets the maxmemory limit (e.g. "100mb", "1gb"). When set, WithMaxMemoryPolicy should also be set to control eviction behaviour.

func WithMaxMemoryPolicy

func WithMaxMemoryPolicy(policy string) testcontainers.ContainerCustomizer

WithMaxMemoryPolicy sets the maxmemory-policy (e.g. "allkeys-lru", "volatile-ttl").

func WithPassword

func WithPassword(password string) testcontainers.ContainerCustomizer

WithPassword sets the Redis AUTH password for all cluster nodes.

func WithReplicasPerMaster

func WithReplicasPerMaster(n int) testcontainers.ContainerCustomizer

WithReplicasPerMaster sets the number of replicas per master node.

func WithoutClusterRequireFullCoverage added in v0.0.2

func WithoutClusterRequireFullCoverage() testcontainers.ContainerCustomizer

WithoutClusterRequireFullCoverage disables the cluster-require-full-coverage check, so the cluster continues serving requests for covered slots even when some hash slots are unassigned. Useful for testing degraded-cluster scenarios.

Types

type Container

type Container struct {
	testcontainers.Container
	// contains filtered or unexported fields
}

Container represents a running Redis cluster in a single Docker container.

func Run

Run starts a Redis cluster container using the given image and options.

func (*Container) AddrMapping added in v0.0.3

func (c *Container) AddrMapping() map[string]string

AddrMapping returns a map from internal addresses (as announced by Redis) to their corresponding host-accessible external addresses.

func (*Container) Addrs

func (c *Container) Addrs() []string

Addrs returns the host-accessible addresses for all cluster nodes.

func (*Container) CurrentNodes added in v0.0.3

func (c *Container) CurrentNodes(ctx context.Context) ([]NodeInfo, error)

CurrentNodes queries the cluster for current topology and returns node information with CurrentRole populated based on the live cluster state. Unlike Nodes(), this method reflects topology changes from failovers.

func (*Container) IsNodeFailed added in v0.0.7

func (c *Container) IsNodeFailed(ctx context.Context, nodeID int) (bool, error)

IsNodeFailed queries cluster topology and returns true if the specified node is marked as fail or pfail (fail?) by other nodes in the cluster. It queries from a different node than the target to avoid blocking on the target.

func (*Container) MapAddress added in v0.0.4

func (c *Container) MapAddress(addr string) (string, bool)

MapAddress translates an internal Redis cluster address (as returned by CLUSTER SLOTS or CLUSTER NODES) to the corresponding external host-accessible address. Returns the original address and false if no mapping exists.

func (*Container) MasterNodes added in v0.0.4

func (c *Container) MasterNodes(ctx context.Context) ([]NodeInfo, error)

MasterNodes returns information about current master nodes in the cluster. This is a convenience wrapper around CurrentNodes() that filters by CurrentRole.

func (*Container) NewDialer

func (c *Container) NewDialer() func(ctx context.Context, network, addr string) (net.Conn, error)

NewDialer returns a dialer that rewrites internal Docker addresses (announced by Redis cluster nodes) to their corresponding host-mapped ports. Pass this dialer to your Redis client so it can connect from outside Docker.

func (*Container) Nodes added in v0.0.3

func (c *Container) Nodes() []NodeInfo

Nodes returns information about all cluster nodes. Note that the returned information reflects the initial cluster configuration at startup. It is not topology-aware: if a failover occurs and a replica is promoted to master, InitialRole will still report "replica". Query CLUSTER SLOTS or CLUSTER NODES via your Redis client if you need current runtime topology.

func (*Container) PauseNode

func (c *Container) PauseNode(ctx context.Context, id int, pendingDuration time.Duration) (resumeFn func() error, err error)

PauseNode sends SIGSTOP to the redis-server process for the given node (0-based index), freezing it without closing connections. If pendingDuration > 0, the node will automatically resume after that duration. The returned resumeFn can be called to resume immediately; it is idempotent.

func (*Container) ReplicaForMaster added in v0.0.5

func (c *Container) ReplicaForMaster(ctx context.Context, masterIndex int) (int, error)

ReplicaForMaster returns the node index of a replica that replicates the given master node, based on current cluster topology. Returns an error if no replica is found or if the master index is invalid.

This is useful for fault injection tests that need to pre-freeze a replica before triggering a failover. By freezing the replica first, then stopping the master, tests can observe the intermediate state where migration has been detected but resubscription is blocked.

func (*Container) StopNode

func (c *Container) StopNode(ctx context.Context, id int, pendingDuration time.Duration) (restartFn func() error, err error)

StopNode stops the redis-server process for the given node (0-based index). If pendingDuration > 0, the node will automatically restart after that duration. The returned restartFn can be called to restart the node immediately; it is idempotent — calling it multiple times or after auto-restart is safe.

type NodeInfo added in v0.0.3

type NodeInfo struct {
	Index        int    // 0-based node index (matches StopNode/PauseNode id parameter)
	InternalAddr string // Address announced by Redis (127.0.0.1:700X)
	ExternalAddr string // Host-accessible address (host:mappedPort)
	InitialRole  string // "master" or "replica" based on startup configuration
	CurrentRole  string // "master" or "replica" based on current topology (only set by CurrentNodes)
	Flags        string // Raw flags from CLUSTER NODES (e.g., "master,fail") (only set by CurrentNodes)
}

NodeInfo describes a single Redis cluster node.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL