minisentinel

package module
v0.0.0-...-920e0bb Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2019 License: BSD-3-Clause Imports: 10 Imported by: 0

README

Minisentinel

Pure Go Redis Sentinel server

Sometimes you want to test code which uses Redis with Sentinel, without making it a full-blown integration test. Minisentinel implements (parts of) the Sentinel protocol to be used in unittests. Used together with Miniredis, they enable a simple, cheap, in-memory, Redis with Sentinel replacement, with a real TCP interface. Think of it as the Sentinel+Redis version of net/http/httptest.

It saves you from using mock code, and since both the sentinel and redis servers live within the test process you can query for values directly, without going through the server stack.

There are no dependencies on external binaries, so you can easily integrate it in automated build processes.

Sentinel Commands

Implemented commands:

  • Connection
    • AUTH -- see RequireAuth()
    • PING
  • Sentinel
    • MASTERS
    • GET-MASTER-ADDR-BY-NAME
    • SLAVES

Redis Commands

See the Miniredis documentation: https://github.com/alicebob/miniredis/blob/master/README.md

Example


package minisentinel

import (
	"errors"
	"testing"
	"time"

	"github.com/FZambia/sentinel"
	"github.com/alicebob/miniredis"
	"github.com/gomodule/redigo/redis"
	"github.com/matryer/is"
)

// TestSomething - shows how-to start up sentinel + redis in your unittest
func TestSomething(t *testing.T) {
	is := is.New(t)
	m := miniredis.NewMiniRedis()
	err := m.StartAddr(":6379")
	is.NoErr(err)
	defer m.Close()
	s := NewSentinel(m, WithReplica(m))
	err = s.StartAddr(":26379")
	is.NoErr(err)
	defer s.Close()
	// all the setup is done.. now just use sentinel/redis like you
	// would normally in your tests via a redis client
}

// TestRedisWithSentinel - an example of combining miniRedis with miniSentinel for unittests
func TestRedisWithSentinel(t *testing.T) {
	is := is.New(t)
	m, err := miniredis.Run()
	m.RequireAuth("super-secret") // not required, but demonstrates how-to
	is.NoErr(err)
	defer m.Close()
	s := NewSentinel(m, WithReplica(m))
	s.Start()
	defer s.Close()

	// use redigo to create a sentinel pool
	sntnl := &sentinel.Sentinel{
		Addrs:      []string{s.Addr()},
		MasterName: s.MasterInfo().Name,
		Dial: func(addr string) (redis.Conn, error) {
			connTimeout := time.Duration(50 * time.Millisecond)
			readWriteTimeout := time.Duration(50 * time.Millisecond)
			c, err := redis.DialTimeout("tcp", addr, connTimeout, readWriteTimeout, readWriteTimeout)
			if err != nil {
				return nil, err
			}
			return c, nil
		},
	}
	redisPassword := []byte("super-secret") // required because of m.RequireAuth()
	pool := redis.Pool{
		MaxIdle:     3,
		MaxActive:   64,
		Wait:        true,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			redisHostAddr, errHostAddr := sntnl.MasterAddr()
			if errHostAddr != nil {
				return nil, errHostAddr
			}
				c, err := redis.Dial("tcp", redisHostAddr)
			if err != nil {
				return nil, err
			}
			if redisPassword != nil { // auth first, before doing anything else
				if _, err := c.Do("AUTH", string(redisPassword)); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			if !sentinel.TestRole(c, "master") {
				return errors.New("Role check failed")
			}
			return nil
		},
	}

	// Optionally set some keys your code expects:
	m.Set("foo", "not-bar")
	m.HSet("some", "other", "key")

	// Run your code and see if it behaves.
	// An example using the redigo library from "github.com/gomodule/redigo/redis":
	c := pool.Get()
	defer c.Close() //release connection back to the pool
	// use the pool connection to do things via TCP to our miniRedis
	_, err = c.Do("SET", "foo", "bar")

	// Optionally check values in redis...
	got, err := m.Get("foo")
	is.NoErr(err)
	is.Equal(got, "bar")

	// ... or use a miniRedis helper for that:
	m.CheckGet(t, "foo", "bar")

	// TTL and expiration:
	m.Set("foo", "bar")
	m.SetTTL("foo", 10*time.Second)
	m.FastForward(11 * time.Second)
	is.True(m.Exists("foo") == false) // it shouldn't be there anymore
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type MasterInfo

type MasterInfo struct {
	Name                  string `mapstructure:"name"`
	IP                    string `mapstructure:"ip"`
	Port                  string `mapstructure:"port"`
	RunID                 string `mapstructure:"runid"`
	Flags                 string `mapstructure:"flags"`
	LinkPendingCommands   string `mapstructure:"link-pending-commands"`
	LinkRefCount          string `mapstructure:"link-refcount"`
	LastPingSent          string `mapstructure:"last-ping-sent"`
	LastOkPingReply       string `mapstructure:"last-ok-ping-reply"`
	LastPingReply         string `mapstructure:"last-ping-reply"`
	DownAfterMilliseconds string `mapstructure:"down-after-milliseconds"`
	InfoRefresh           string `mapstructure:"info-refresh"`
	RoleReported          string `mapstructure:"role-reported"`
	RoleReportedTime      string `mapstructure:"role-reported-time"`
	ConfigEpoch           string `mapstructure:"config-epoch"`
	NumSlaves             string `mapstructure:"num-slaves"`
	NumOtherSentinels     string `mapstructure:"num-other-sentinels"`
	Quorum                string `mapstructure:"quorum"`
	FailoverTimeout       string `mapstructure:"failover-timeout"`
	ParallelSync          string `mapstructure:"parallel-syncs"`
}

MasterInfo - define a redis master

func NewMasterInfoFromStrings

func NewMasterInfoFromStrings(s []string) (MasterInfo, error)

NewMasterInfoFromStrings creates a new MasterInfo

type Option

type Option func(*Options)

Option - how Options are passed as arguments

func WithMaster

func WithMaster(m *miniredis.Miniredis) Option

WithMaster - set the primary miniredis for the sentinel

func WithMasterName

func WithMasterName(name string) Option

WithMasterName - set the name of the master

func WithReplica

func WithReplica(replica *miniredis.Miniredis) Option

WithReplica - set the replicas for sentinel

type Options

type Options struct {
	// contains filtered or unexported fields
}

Options = how options are represented

func GetOpts

func GetOpts(opt ...Option) Options

GetOpts - iterate the inbound Options and return a struct

type ReplicaInfo

type ReplicaInfo struct {
	Name                  string `mapstructure:"name"`
	IP                    string `mapstructure:"ip"`
	Port                  string `mapstructure:"port"`
	RunID                 string `mapstructure:"runid"`
	Flags                 string `mapstructure:"flags"`
	LinkPendingCommands   string `mapstructure:"link-pending-commands"`
	LinkRefCount          string `mapstructure:"link-refcount"`
	LastPingSent          string `mapstructure:"last-ping-sent"`
	LastOkPingReply       string `mapstructure:"last-ok-ping-reply"`
	LastPingReply         string `mapstructure:"last-ping-reply"`
	DownAfterMilliseconds string `mapstructure:"down-after-milliseconds"`
	InfoRefresh           string `mapstructure:"info-refresh"`
	RoleReported          string `mapstructure:"role-reported"`
	RoleReportedTime      string `mapstructure:"role-reported-time"`
	MasterLinkDownTime    string `mapstructure:"master-link-down-time"`
	MasterLinkStatus      string `mapstructure:"master-link-status"`
	MasterHost            string `mapstructure:"master-host"`
	MasterPort            string `mapstructure:"master-port"`
	SlavePriority         string `mapstructure:"slave-priority"`
	SlaveReplOffset       string `mapstructure:"slave-repl-offset"`
}

ReplicaInfo - define a redis master

func NewReplicaInfoFromStrings

func NewReplicaInfoFromStrings(s []string) (ReplicaInfo, error)

NewReplicaInfoFromStrings creates a new ReplicaInfo

type Sentinel

type Sentinel struct {
	sync.Mutex
	// contains filtered or unexported fields
}

Sentinel - a redis sentinel server implementation.

func NewSentinel

func NewSentinel(master *miniredis.Miniredis, opts ...Option) *Sentinel

NewSentinel makes a new, non-started, Miniredis object.

func Run

func Run(master *miniredis.Miniredis, opts ...Option) (*Sentinel, error)

Run creates and Start()s a Sentinel.

func (*Sentinel) Addr

func (s *Sentinel) Addr() string

Addr returns '127.0.0.1:12345'. Can be given to a Dial(). See also Host() and Port(), which return the same things.

func (*Sentinel) Close

func (s *Sentinel) Close()

Close shuts down a Sentinel.

func (*Sentinel) CurrentConnectionCount

func (s *Sentinel) CurrentConnectionCount() int

CurrentConnectionCount returns the number of currently connected clients.

func (*Sentinel) Host

func (s *Sentinel) Host() string

Host returns the host part of Addr().

func (*Sentinel) Master

func (s *Sentinel) Master() *miniredis.Miniredis

Master - get the master

func (*Sentinel) MasterInfo

func (s *Sentinel) MasterInfo(opts ...Option) MasterInfo

MasterInfo - get the master's info

func (*Sentinel) Port

func (s *Sentinel) Port() string

Port returns the (random) port part of Addr().

func (*Sentinel) Replica

func (s *Sentinel) Replica() *miniredis.Miniredis

Replica - get the current replica

func (*Sentinel) ReplicaInfo

func (s *Sentinel) ReplicaInfo(opts ...Option) ReplicaInfo

ReplicaInfo - get the replica's info

func (*Sentinel) RequireAuth

func (s *Sentinel) RequireAuth(pw string)

RequireAuth makes every connection need to AUTH first. Disable again by setting an empty string.

func (*Sentinel) Restart

func (s *Sentinel) Restart() error

Restart restarts a Close()d server on the same port. Values will be preserved.

func (*Sentinel) SetReplica

func (s *Sentinel) SetReplica(replica *miniredis.Miniredis)

SetReplica - replace all the existing replicas

func (*Sentinel) Start

func (s *Sentinel) Start() error

Start starts a server. It listens on a random port on localhost. See also Addr().

func (*Sentinel) StartAddr

func (s *Sentinel) StartAddr(addr string) error

StartAddr runs sentinel with a given addr. Examples: "127.0.0.1:26379", ":6379", or "127.0.0.1:0"

func (*Sentinel) TotalConnectionCount

func (s *Sentinel) TotalConnectionCount() int

TotalConnectionCount returns the number of client connections since server start.

func (*Sentinel) WithMaster

func (s *Sentinel) WithMaster(m *miniredis.Miniredis, opts ...Option)

WithMaster - set the master

Jump to

Keyboard shortcuts

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