pkg.go.dev

Package radix implements all functionality needed to work with redis and all things related to it, including redis cluster, pubsub, sentinel, scanning, lua scripting, and more.

This package has extensive examples documenting advanced behavior not covered here.

Creating a Client

For a single redis instance use PoolConfig to create a connection pool. The connection pool implements the Client interface. It is thread-safe and will automatically create, reuse, and recreate connections as needed:

client, err := (radix.PoolConfig{}).New("tcp", "127.0.0.1:6379")
if err != nil {
	// handle error
}

If you're using sentinel or cluster you should use SentinelConfig or ClusterConfig (respectively) to create your Client instead.

Commands

Any redis command can be performed by passing a Cmd into a Client's Do method. Each Cmd instance should only be used once. The return from the Cmd can be captured into any appopriate go primitive type, or a slice, map, or struct, if the command returns an array.

// discard the result
err := client.Do(ctx, radix.Cmd(nil, "SET", "foo", "someval"))
var fooVal string
err := client.Do(ctx, radix.Cmd(&fooVal, "GET", "foo"))
var fooValB []byte
err := client.Do(ctx, radix.Cmd(&fooValB, "GET", "foo"))
var barI int
err := client.Do(ctx, radix.Cmd(&barI, "INCR", "bar"))
var bazEls []string
err := client.Do(ctx, radix.Cmd(&bazEls, "LRANGE", "baz", "0", "-1"))
var buzMap map[string]string
err := client.Do(ctx, radix.Cmd(&buzMap, "HGETALL", "buz"))

FlatCmd can also be used if you wish to use non-string arguments like integers, slices, maps, or structs, and have them automatically be flattened into a single string slice.

Other Actions

Cmd and FlatCmd both implement the Action interface. Other Actions include Pipeline, WithConn, and EvalScript.Cmd. Any of these may be passed into any Client's Do method.

var fooVal string
p := radix.NewPipeline()
p.Append(radix.FlatCmd(nil, "SET", "foo", 1))
p.Append(radix.Cmd(&fooVal, "GET", "foo"))
if err := client.Do(p); err != nil {
	panic(err)
}
fmt.Printf("fooVal: %q\n", fooVal)

Transactions

There are two ways to perform transactions in redis. The first is with the MULTI/EXEC commands, which can be done using the WithConn Action (see its example). The second is using EVAL with lua scripting, which can be done using the EvalScript Action (again, see its example).

EVAL with lua scripting is recommended in almost all cases. It only requires a single round-trip, it's infinitely more flexible than MULTI/EXEC, it's simpler to code, and for complex transactions, which would otherwise need a WATCH statement with MULTI/EXEC, it's significantly faster.

AUTH and other settings via Dialer

Dialer has fields like AuthPass and SelectDB which can be used to configure Conns at creation.

PoolConfig takes a Dialer as one of its fields, so that all Conns the Pool creates will be created with those settings.

Other Clients which create their own Pools, like Cluster and Sentinel, will take in a PoolConfig which can be used to configure the Pools they create.

For example, to create a Cluster instance which uses a particular AUTH password on all Conns:

cfg := radix.ClusterConfig{
	PoolConfig: radix.PoolConfig{
		Dialer: radix.Dialer{
			AuthPass: "mySuperSecretPassword",
		},
	},
}
client, err := cfg.New(ctx, []string{redisAddr1, redisAddr2, redisAddr3})

Custom implementations

All interfaces in this package were designed such that they could have custom implementations. There is no dependency within radix that demands any interface be implemented by a particular underlying type, so feel free to create your own Pools or Conns or Actions or whatever makes your life easier.

Errors

Errors returned from redis can be explicitly checked for using the the resp3.SimpleError type. Note that the errors.As or errors.Is functions, introduced in go 1.13, should be used.

var redisErr resp3.SimpleError
err := client.Do(ctx, radix.Cmd(nil, "AUTH", "wrong password"))
if errors.As(err, &redisErr) {
	log.Printf("redis error returned: %s", redisErr.S)
}

This section is empty.

ErrNoStreamEntries is returned by StreamReader's Next method to indicate that there were no stream entries left to be read.

CRC16 returns checksum for a given set of bytes based on the crc algorithm defined for hashing redis keys in a cluster setup.

ClusterSlot returns the slot number the key belongs to in any redis cluster, taking into account key hash tags.

Action performs a task using a Conn.

Cmd is used to perform a redis command and retrieve a result. It should not be passed into Do more than once.

If the receiver value of Cmd is nil then the result is discarded.

If the receiver value of Cmd is a primitive, a slice/map, or a struct then a pointer must be passed in. It may also be an io.Writer, an encoding.Text/BinaryUnmarshaler, or a resp.Unmarshaler.

The Action returned by Cmd also implements resp.Marshaler.

See CmdConfig's documentation if more configurability is required, e.g. if using commands provided by Redis Modules.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	panic(err)
}
if err := client.Do(ctx, Cmd(nil, "SET", "foo", "bar")); err != nil {
	panic(err)
}
var fooVal string
if err := client.Do(ctx, Cmd(&fooVal, "GET", "foo")); err != nil {
	panic(err)
}
fmt.Println(fooVal)
Output:
bar
func FlatCmd(rcv interface{}, cmd string, args ...interface{}) Action

FlatCmd is like Cmd, but the arguments can be of almost any type, and FlatCmd will automatically flatten them into a single array of strings. Like Cmd, a FlatCmd should not be passed into Do more than once.

FlatCmd supports using a resp.LenReader (an io.Reader with a Len() method) as an argument. *bytes.Buffer is an example of a LenReader, and the resp package has a NewLenReader function which can wrap an existing io.Reader.

FlatCmd supports encoding.Text/BinaryMarshalers, big.Float, and big.Int.

The receiver to FlatCmd follows the same rules as for Cmd.

The Action returned by FlatCmd implements resp.Marshaler.

See CmdConfig's documentation if more configurability is required, e.g. if using commands provided by Redis Modules.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	panic(err)
}
// performs "SET" "foo" "1"
err = client.Do(ctx, FlatCmd(nil, "SET", "foo", 1))
if err != nil {
	panic(err)
}
// performs "SADD" "fooSet" "1" "2" "3"
err = client.Do(ctx, FlatCmd(nil, "SADD", "fooSet", []string{"1", "2", "3"}))
if err != nil {
	panic(err)
}
// performs "HMSET" "foohash" "a" "1" "b" "2" "c" "3"
m := map[string]int{"a": 1, "b": 2, "c": 3}
err = client.Do(ctx, FlatCmd(nil, "HMSET", "fooHash", m))
if err != nil {
	panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	panic(err)
}
// FlatCmd can marshal structs into a key/value array. Exported field names
// will be used as keys, using similar rules as the json package.
type ExampleStruct struct {
	Foo string // The key "Foo" will be used.
	Bar string `redis:"BAR"` // The key "BAR" will be used
	Baz string `redis:"-"`   // This field will be skipped
}
type OuterExampleStruct struct {
	// adds fields "Foo" and "BAR" to OuterExampleStruct
	ExampleStruct
	Biz int
}
s := OuterExampleStruct{
	ExampleStruct: ExampleStruct{
		Foo: "1",
		Bar: "2",
		Baz: "3",
	},
	Biz: 4,
}
err = client.Do(ctx, FlatCmd(nil, "HMSET", "barHash", s))
if err != nil {
	panic(err)
}
// Cmd and FlatCmd can also unmarshal results into a struct.
var s2 OuterExampleStruct
err = client.Do(ctx, Cmd(&s2, "HGETALL", "barHash"))
if err != nil {
	panic(err)
}
fmt.Printf("s2: %+v\n", s2)
Output:
s2: {ExampleStruct:{Foo:1 Bar:2 Baz:} Biz:4}

WithConn is used to perform a set of independent Actions on the same Conn.

key should be a key which one or more of the inner Actions is going to act on, or "" if no keys are being acted on or the keys aren't yet known. key is generally only necessary when using Cluster.

The callback function is what should actually carry out the inner actions, and the error it returns will be passed back up immediately.

NOTE that WithConn only ensures all inner Actions are performed on the same Conn, it doesn't make them transactional. Use MULTI/WATCH/EXEC within a WithConn for transactions, or use EvalScript.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
// This example retrieves the current integer value of `key` and sets its
// new value to be the increment of that, all using the same connection
// instance. NOTE that it does not do this atomically like the INCR command
// would.
key := "someKey"
err = client.Do(ctx, WithConn(key, func(ctx context.Context, conn Conn) error {
	var curr int
	if err := conn.Do(ctx, Cmd(&curr, "GET", key)); err != nil {
		return err
	}
	curr++
	return conn.Do(ctx, FlatCmd(nil, "SET", key, curr))
}))
if err != nil {
	// handle error
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
// This example retrieves the current value of `key` and then sets a new
// value on it in an atomic transaction.
key := "someKey"
var prevVal string
err = client.Do(ctx, WithConn(key, func(ctx context.Context, c Conn) error {
	// Begin the transaction with a MULTI command
	if err := c.Do(ctx, Cmd(nil, "MULTI")); err != nil {
		return err
	}
	// If any of the calls after the MULTI call error it's important that
	// the transaction is discarded. This isn't strictly necessary if the
	// only possible error is a network error, as the connection would be
	// closed by the client anyway.
	var err error
	defer func() {
		if err != nil {
			// The return from DISCARD doesn't matter. If it's an error then
			// it's a network error and the Conn will be closed by the
			// client.
			_ = c.Do(ctx, Cmd(nil, "DISCARD"))
		}
	}()
	// queue up the transaction's commands
	if err = c.Do(ctx, Cmd(nil, "GET", key)); err != nil {
		return err
	}
	if err = c.Do(ctx, Cmd(nil, "SET", key, "someOtherValue")); err != nil {
		return err
	}
	// execute the transaction, capturing the result in a Tuple. We only
	// care about the first element (the result from GET), so we discard the
	// second by setting nil.
	result := Tuple{&prevVal, nil}
	return c.Do(ctx, Cmd(&result, "EXEC"))
}))
if err != nil {
	// handle error
}
fmt.Printf("the value of key %q was %q\n", key, prevVal)
type ActionProperties struct {


	Keys []string


	CanRetry bool


	CanPipeline bool


	CanShareConn bool
}

ActionProperties describes various properties of an Action. It should be expected that more fields will be added to this struct as time goes forward, though the zero values of those new fields will have sane default behaviors.

DefaultActionProperties returns an ActionProperties instance for the given Redis command and it's argument.

The returned ActionProperties should work well with all standard Redis commands, including allowing the command to be used in pipelines and for connection sharing if the command is non-blocking, but may not return correct results for custom commands provided by Redis Modules or unreleased commands.

Client describes an entity which can carry out Actions on a single redis instance. Conn and Pool are Clients.

Implementations of Client are expected to be thread-safe.

type Cluster struct {
}

Cluster is a MultiClient which contains all information about a redis cluster needed to interact with it, including a set of pools to each of its instances.

All methods on Cluster are thread-safe.

Cluster will automatically attempt to handle MOVED/ASK errors.

Clients implements the method for the MultiClient interface.

Close cleans up all goroutines spawned by Cluster and closes all of its Pools.

Do performs an Action on a redis instance in the cluster, with the instance being determeined by the keys returned from the Action's Properties() method.

This method handles MOVED and ASK errors automatically in most cases.

DoSecondary implements the method for the MultiClient interface. It will perform the Action on a random secondary for the affected keys, or the primary if no secondary is available.

For DoSecondary to work, all connections must be created in read-only mode by using the READONLY command. See the PoolConfig field of ClusterConfig for more details.

Sync will synchronize the Cluster with the actual cluster, making new pools to new instances and removing ones from instances no longer in the cluster. This will be called periodically automatically, but you can manually call it at any time as well.

func (c *Cluster) Topo() ClusterTopo

Topo returns the Cluster's topology as it currently knows it. See ClusterTopo's docs for more on its default order.

ClusterConfig is used to create Cluster instances with particular settings. All fields are optional, all methods are thread-safe.

New initializes and returns a Cluster instance using the ClusterConfig. It will try every address given until it finds a usable one. From there it uses CLUSTER SLOTS to discover the cluster topology and make all the necessary connections.

type ClusterNode struct {


	Addr, ID string

	Slots [][2]uint16

	SecondaryOfAddr, SecondaryOfID string
}

ClusterNode describes a single node in a redis cluster at a moment in time.

type ClusterTopo []ClusterNode

ClusterTopo describes the topology of a redis cluster at a given moment. It will be sorted first by slot number of each node and then by secondary status, so primaries will come before secondaries.

Map returns the topology as a mapping of node address to its ClusterNode.

MarshalRESP implements the resp.Marshaler interface, and will marshal the ClusterTopo in the same format as the return from CLUSTER SLOTS.

func (tt ClusterTopo) Primaries() ClusterTopo

Primaries returns a ClusterTopo instance containing only the primary nodes from the ClusterTopo being called on.

UnmarshalRESP implements the resp.Unmarshaler interface, but only supports unmarshaling the return from CLUSTER SLOTS. The unmarshaled nodes will be sorted before they are returned.

type CmdConfig struct {


	ActionProperties func(cmd string, args ...string) ActionProperties
}

CmdConfig is used to create redis command Actions with particular settings. All fields are optional, all methods are thread-safe.

The global Cmd and FlatCmd functions are shortcuts for using an empty CmdConfig{}.

This can be useful for working with custom commands provided by Redis modules for which the built in logic may return sub-optimal properties or in case some commands are known to be slow in some cases and therefore should not use connection sharing.

All methods on CmdConfig are safe for concurrent use from different goroutines.

ExampleCmdConfig_Cmd shows how to use the CmdConfig type to handle an imaginary CUSTOM.MZADD command which allows adding members to multiple sorted sets at once.

The assumed syntax for the command is:

CUSTOM.MZADD key score member [key score member ...]
cfg := CmdConfig{
	// Specify a custom ActionProperties resolver which will be called for
	// actions created using cfg.
	ActionProperties: func(cmd string, args ...string) ActionProperties {
		keys := make([]string, 0, len(args)/3)
		// The first value of each pair of 3 arguments is the key.
		for i := 0; i < len(args); i += 3 {
			keys = append(keys, args[i])
		}
		return ActionProperties{
			Keys:         keys,
			CanRetry:     true,
			CanPipeline:  true,
			CanShareConn: true,
		}
	},
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
// Use our cfg type to created the Action instead of the global Cmd function.
if err := client.Do(ctx, cfg.Cmd(nil, "CUSTOM.MZADD",
	"key1", "100", "member1",
	"key2", "200", "member2",
	"key3", "300", "member3")); err != nil {
	// handle error
}

Cmd works like the global Cmd function but can be additionally configured using fields on CmdConfig. See the global Cmd's documentation for further details.

func (cfg CmdConfig) FlatCmd(rcv interface{}, cmd string, args ...interface{}) Action

FlatCmd works like the global FlatCmd function but can be additionally configured using fields on CmdConfig. See the global FlatCmd's documentation for further details.

type Conn interface {
	Client


	EncodeDecode(ctx context.Context, marshal, unmarshalInto interface{}) error
}

Conn is a Client wrapping a single network connection which synchronously reads/writes data using redis's RESP protocol.

A Conn can be used directly as a Client, but in general you probably want to use a Pool instead.

Dial is a shortcut for calling Dial on a zero-value Dialer.

NewPubSubConnStub returns a stubbed Conn, much like NewStubConn does, which pretends it is a Conn to a real redis instance, but is instead using the given callback to service requests. It is primarily useful for writing tests.

NewPubSubConnStub differs from NewStubConn in that EncodeDecode calls for the (P)SUBSCRIBE, (P)UNSUBSCRIBE, and PING commands will be intercepted and handled as per redis' expected pubsub functionality. A PubSubMessage may be written to the returned channel at any time, and if the returned Conn has had (P)SUBSCRIBE called matching that PubSubMessage then the PubSubMessage will be written to the Conn's internal buffer.

This is intended to be used for mocking services which can perform both normal redis commands and pubsub (e.g. a real redis instance, redis sentinel). The returned Conn can be passed into NewPubSubConn.

remoteNetwork and remoteAddr can be empty, but if given will be used as the return from the Addr method.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Make a pubsub stub conn which will return nil for everything except
// pubsub commands (which will be handled automatically)
stub, stubCh := NewPubSubConnStub("tcp", "127.0.0.1:6379", func(context.Context, []string) interface{} {
	return nil
})
// These writes shouldn't do anything, initially, since we haven't
// subscribed to anything
go func() {
	for {
		stubCh <- PubSubMessage{
			Channel: "foo",
			Message: []byte("bar"),
		}
		time.Sleep(1 * time.Second)
	}
}()
// Use PubSubConfig to wrap the stub like we would for a normal redis
// connection
pstub := (PubSubConfig{}).New(stub)
// Subscribe to "foo"
if err := pstub.Subscribe(ctx, "foo"); err != nil {
	panic(err)
}
// now msgCh is subscribed the publishes being made by the go-routine above
// will start being written to it
for {
	msg, err := pstub.Next(ctx)
	if errors.Is(err, context.DeadlineExceeded) {
		break
	} else if err != nil {
		panic(err)
	}
	log.Printf("read msg: %#v", msg)
}

NewStubConn returns a (fake) Conn which pretends it is a Conn to a real redis instance, but is instead using the given callback to service requests. It is primarily useful for writing tests.

When EncodeDecode is called the value to be marshaled is converted into a []string and passed to the callback. The return from the callback is then marshaled into an internal buffer. The value to be decoded is unmarshaled into using the internal buffer. If the internal buffer is empty at this step then the call will block.

remoteNetwork and remoteAddr can be empty, but if given will be used as the return from the Addr method.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
m := map[string]string{}
stub := NewStubConn("tcp", "127.0.0.1:6379", func(_ context.Context, args []string) interface{} {
	switch args[0] {
	case "GET":
		return m[args[1]]
	case "SET":
		m[args[1]] = args[2]
		return nil
	default:
		return fmt.Errorf("this stub doesn't support command %q", args[0])
	}
})
if err := stub.Do(ctx, Cmd(nil, "SET", "foo", "1")); err != nil {
	panic(err)
}
var foo int
if err := stub.Do(ctx, Cmd(&foo, "GET", "foo")); err != nil {
	panic(err)
}
fmt.Printf("foo: %d\n", foo)

Dialer is used to create Conns with particular settings. All fields are optional, all methods are thread-safe.

Dial creates a Conn using the Dialer configuration.

In place of a host:port address, Dial also accepts a URI, as per:

https://www.iana.org/assignments/uri-schemes/prov/redis

If the URI has an AUTH password or db specified Dial will attempt to perform the AUTH and/or SELECT as well.

type EvalScript struct {
}

EvalScript contains the body of a script to be used with redis' EVAL functionality. Call Cmd on a EvalScript to actually create an Action which can be run.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// set as a global variable, this script is equivalent to the builtin GETSET
// redis command
var getSet = NewEvalScript(`
		local prev = redis.call("GET", KEYS[1])
		redis.call("SET", KEYS[1], ARGV[1])
		return prev
`)
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
key := "someKey"
var prevVal string
if err := client.Do(ctx, getSet.Cmd(&prevVal, []string{key}, "myVal")); err != nil {
	// handle error
}
fmt.Printf("value of key %q used to be %q\n", key, prevVal)

NewEvalScript initializes a EvalScript instance with the given script.

Cmd is like the top-level Cmd but it uses the the EvalScript to perform an EVALSHA command (and will automatically fallback to EVAL as necessary).

func (es EvalScript) FlatCmd(rcv interface{}, keys []string, args ...interface{}) Action

FlatCmd is like the top level FlatCmd except it uses the EvalScript to perform an EVALSHA command (and will automatically fallback to EVAL as necessary).

type Maybe struct {

	Rcv interface{}

	Null bool


	Empty bool
}

Maybe is a type which wraps a receiver being unmarshaled into. When unmarshaling takes place Maybe will also populate its other fields accordingly.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
var rcv int64
mb := Maybe{Rcv: &rcv}
if err := client.Do(ctx, Cmd(&mb, "GET", "foo")); err != nil {
	// handle error
} else if mb.Null {
	fmt.Println("rcv is null")
} else {
	fmt.Printf("rcv is %d\n", rcv)
}

UnmarshalRESP implements the method for the resp.Unmarshaler interface.

MultiClient wraps one or more underlying Clients for different redis instances. MultiClient methods are thread-safe and may return the same Client instance to different callers at the same time. All returned Clients should _not_ have Close called on them.

If the topology backing a MultiClient changes (e.g. a failover occurs) while the Clients it returned are still being used then those Clients may return errors related to that change.

Sentinel and Cluster are both MultiClients.

func NewMultiClient(rs ReplicaSet) MultiClient

NewMultiClient wraps a ReplicaSet such that it implements MultiClient.

PersistentPubSubConnConfig is used to create a persistent PubSubConn with particular settings. All fields are optional, all methods are thread-safe.

// Example of how to use a persistent PubSubConn with a Cluster.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Initialize the cluster in any way you see fit
cluster, err := (ClusterConfig{}).New(ctx, []string{"127.0.0.1:6379"})
if err != nil {
	panic(err)
}
// Have PersistentPubSubConfig pick a random cluster node everytime it wants
// to make a new connection. If the node fails PersistentPubSubConfig will
// automatically pick a new node to connect to.
pconn, err := (PersistentPubSubConnConfig{}).New(ctx, func() (string, string, error) {
	clients, err := cluster.Clients()
	if err != nil {
		return "", "", err
	}
	for addr := range clients {
		return "tcp", addr, nil
	}
	return "", "", errors.New("no clients in the cluster")
})
if err != nil {
	panic(err)
}
// Use the PubSubConn as normal.
if err := pconn.Subscribe(ctx, "myChannel"); err != nil {
	panic(err)
}
for {
	msg, err := pconn.Next(ctx)
	if errors.Is(err, context.DeadlineExceeded) {
		break
	} else if err != nil {
		panic(err)
	}
	log.Printf("publish to channel %q received: %q", msg.Channel, msg.Message)
}

New is like PubSubConfig.New, but instead of taking in an existing Conn to wrap it will create its own using the network/address returned from the given callback.

If the Conn is ever severed then the callback will be re-called, a new Conn will be created, and that Conn will be reset to the previous Conn's state.

This is effectively a way to have a permanent PubSubConn established which supports subscribing/unsubscribing but without the hassle of implementing reconnect/re-subscribe logic.

type Pipeline struct {
}

Pipeline is an Action which combines multiple commands into a single network round-trip. Pipeline accumulates commands via its Append method. When Pipeline is performed (i.e. passed into a Client's Do method) it will first write all commands as a single write operation and then read all command responses with a single read operation.

Pipeline may be Reset in order to re-use an instance for multiple sets of commands. A Pipeline may _not_ be performed multiple times without being Reset in between.

NOTE that, while a Pipeline performs all commands on a single Conn, it shouldn't be used by itself for MULTI/EXEC transactions, because if there's an error it won't discard the incomplete transaction. Use WithConn or EvalScript for transactional functionality instead.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379") // or any other client
if err != nil {
	// handle error
}
var fooVal string
p := NewPipeline()
p.Append(FlatCmd(nil, "SET", "foo", 1))
p.Append(Cmd(&fooVal, "GET", "foo"))
if err := client.Do(ctx, p); err != nil {
	// handle error
}
fmt.Printf("fooVal: %q\n", fooVal)
// At this point the Pipeline cannot be used again, unless Reset is called.
var barVal string
p.Reset()
p.Append(FlatCmd(nil, "SET", "bar", 2))
p.Append(Cmd(&barVal, "GET", "bar"))
if err := client.Do(ctx, p); err != nil {
	// handle error
}
fmt.Printf("barVal: %q\n", barVal)
Output:
fooVal: "1"
barVal: "2"
func NewPipeline() *Pipeline

NewPipeline returns a Pipeline instance to which Actions can be Appended.

func (p *Pipeline) Append(a Action)

Append adds the Action to the end of the list of Actions to pipeline together. This will panic if given an Action without the CanPipeline property set to true.

Perform implements the method for the Action interface.

func (p *Pipeline) Properties() ActionProperties

Properties implements the method for the Action interface.

func (p *Pipeline) Reset()

Reset discards all Actions and resets all internal state. A Pipeline with Reset called on it is equivalent to one returned by NewPipeline.

PoolConfig is used to create Pool instances with particular settings. All fields are optional, all methods are thread-safe.

New creates and returns a pool instance using the PoolConfig.

PubSubConfig is used to create a PubSubConn with particular settings. All fields are optional, all methods are thread-safe.

func (cfg PubSubConfig) New(conn Conn) PubSubConn

New returns a PubSubConn instance using the given PubSubConfig.

PubSubConn wraps an existing Conn to support redis' pubsub system. Unlike Conn, a PubSubConn's methods are _not_ thread-safe.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Create a normal redis connection
conn, err := Dial(ctx, "tcp", "127.0.0.1:6379")
if err != nil {
	panic(err)
}
// Pass that connection into PubSub, conn should never get used after this
pconn := (PubSubConfig{}).New(conn)
defer pconn.Close() // this will close Conn as well
// Subscribe to a channel called "myChannel".
if err := pconn.Subscribe(ctx, "myChannel"); err != nil {
	panic(err)
}
for {
	msg, err := pconn.Next(ctx)
	if errors.Is(err, context.DeadlineExceeded) {
		break
	} else if err != nil {
		panic(err)
	}
	log.Printf("publish to channel %q received: %q", msg.Channel, msg.Message)
}

PubSubMessage describes a message being published to a redis pubsub channel.

MarshalRESP implements the Marshaler interface.

UnmarshalRESP implements the Unmarshaler interface.

type ReplicaSet struct {
	Primary     Client
	Secondaries []Client
}

ReplicaSet holds the Clients of a redis replica set, consisting of a single primary (read+write) instance and zero or more secondary (read-only) instances.

Scanner is used to iterate through the results of a SCAN call (or HSCAN, SSCAN, etc...)

Once created, repeatedly call Next() on it to fill the passed in string pointer with the next result. Next will return false if there's no more results to retrieve or if an error occurred, at which point Close should be called to retrieve any error.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Initialize the cluster in any way you see fit
cluster, err := (ClusterConfig{}).New(ctx, []string{"127.0.0.1:6379"})
if err != nil {
	panic(err)
}
s := (ScannerConfig{Command: "SCAN"}).NewMulti(cluster)
var key string
for s.Next(ctx, &key) {
	log.Printf("key: %q", key)
}
if err := s.Close(); err != nil {
	log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6739")
if err != nil {
	log.Fatal(err)
}
s := (ScannerConfig{Command: "HSCAN", Key: "somekey"}).New(client)
var key string
for s.Next(ctx, &key) {
	log.Printf("key: %q", key)
}
if err := s.Close(); err != nil {
	log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := (PoolConfig{}).New(ctx, "tcp", "127.0.0.1:6379")
if err != nil {
	log.Fatal(err)
}
s := (ScannerConfig{}).New(client)
var key string
for s.Next(ctx, &key) {
	log.Printf("key: %q", key)
}
if err := s.Close(); err != nil {
	log.Fatal(err)
}

ScannerConfig is used to create Scanner instances with particular settings. All fields are optional, all methods are thread-safe.

func (cfg ScannerConfig) New(c Client) Scanner

New creates a new Scanner instance which will iterate over the redis instance's Client using the ScannerConfig.

func (cfg ScannerConfig) NewMulti(mc MultiClient) Scanner

NewMulti returns a Scanner which will scan over every primary instance in the MultiClient. This will panic if the ScanOpt's Command isn't "SCAN".

NOTE this is primarily useful for scanning over all keys in a Cluster. It is not necessary to use this otherwise, unless you have implemented your own MultiClient which holds multiple primary Clients.

type Sentinel struct {
}

Sentinel is a MultiClient which contains all information needed to interact with a redis replica set managed by redis sentinel, including a set of pools to each of its instances. All methods on Sentinel are thread-safe.

Clients implements the method for the MultiClient interface. The returned map will only ever have one key/value pair.

Close implements the method for the Client interface.

Do implements the method for the Client interface. It will perform the given Action on the current primary.

DoSecondary implements the method for the Client interface. It will perform the given Action on a random secondary, or the primary if no secondary is available.

For DoSecondary to work, replicas must be configured with replica-read-only enabled, otherwise calls to DoSecondary may by rejected by the replica.

SentinelAddrs returns the addresses of all known sentinels.

SentinelConfig is used to create Sentinel instances with particular settings. All fields are optional, all methods are thread-safe.

New creates and returns a *Sentinel instance using the SentinelConfig.

type StreamConfig struct {


	After StreamEntryID


	Latest bool


	PendingThenLatest bool
}

StreamConfig is used to configure the reading behavior of individual streams being read by a StreamReader. Exactly one field should be filled in.

type StreamEntries struct {
	Stream  string
	Entries []StreamEntry
}

StreamEntries is a stream name and set of entries as returned by XREAD and XREADGROUP. The results from a call to XREAD(GROUP) can be unmarshaled into a []StreamEntries.

UnmarshalRESP implements the resp.Unmarshaler interface.

type StreamEntry struct {

	ID StreamEntryID

	Fields [][2]string
}

StreamEntry is an entry in a stream as returned by XRANGE, XREAD and XREADGROUP.

UnmarshalRESP implements the resp.Unmarshaler interface.

StreamEntryID represents an ID used in a Redis stream with the format <time>-<seq>.

Before returns true if s comes before o in a stream (is less than o).

MarshalRESP implements the resp.Marshaler interface.

func (s StreamEntryID) Next() StreamEntryID

Next returns the next stream entry ID or s if there is no higher id (s is 18446744073709551615-18446744073709551615).

func (s StreamEntryID) Prev() StreamEntryID

Prev returns the previous stream entry ID or s if there is no prior id (s is 0-0).

String returns the ID in the format <time>-<seq> (the same format used by Redis).

String implements the fmt.Stringer interface.

UnmarshalRESP implements the resp.Unmarshaler interface.

StreamReader allows reading StreamEntrys sequentially from one or more streams.

StreamReaderConfig is used to create StreamReader instances with particular settings. All fields are optional, all methods are thread-safe.

New returns a new StreamReader for the given Client. The StreamReader will read from the streams given as the keys of the map.

type Tuple []interface{}

Tuple is a helper type which can be used when unmarshaling a RESP array. Each element of Tuple should be a pointer receiver which the corresponding element of the RESP array will be unmarshaled into, or nil to skip that element. The length of Tuple must match the length of the RESP array being unmarshaled.

Tuple is useful when unmarshaling the results from commands like EXEC and EVAL.

UnmarshalRESP implements the method for the resp.Unmarshaler interface.

Read the original on pkg.go.dev ↗