rueidis

package module
v0.0.0-...-c14f4f2 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2022 License: Apache-2.0 Imports: 24 Imported by: 0

README

rueidis

Go Reference Build status Go Report Card codecov Total alerts Maintainability

A Fast Golang Redis client that does auto pipelining and supports client side caching.

Features

  • Auto pipeline for non-blocking redis commands
  • Connection pooling for blocking redis commands
  • Opt-in client side caching in RESP3
  • Pub/Sub, Redis 7 Sharded Pub/Sub
  • Redis Cluster, Sentinel, Streams, TLS, RedisJSON, RedisBloom, RediSearch, RedisGraph, RedisTimeseries, RedisAI, RedisGears
  • IDE friendly redis command builder
  • Generic Hash/RedisJSON Object Mapping with client side caching and optimistic locking
  • OpenTelemetry tracing and metrics
  • Distributed Locks with client side caching
  • Helpers for writing tests with rueidis mock

Limitations

Rueidis is built around RESP2 and RESP3 protocol, and supports almost all redis features. However, the following features has not yet been implemented in RESP2 mode:

  • Client side caching only works in RESP3 and Redis >= 6.0

Getting Started

package main

import (
	"context"
	"github.com/rueian/rueidis"
)

func main() {
	c, err := rueidis.NewClient(rueidis.ClientOption{
		InitAddress: []string{"127.0.0.1:6379"},
	})
	if err != nil {
		panic(err)
	}
	defer c.Close()

	ctx := context.Background()

	// SET key val NX
	c.Do(ctx, c.B().Set().Key("key").Value("val").Nx().Build()).Error()
	// GET key
	c.Do(ctx, c.B().Get().Key("key").Build()).ToString()
}

Auto Pipeline

All non-blocking commands sending to a single redis node are automatically pipelined through connections, which reduces the overall round trips and system calls, and gets higher throughput.

Benchmark comparison with go-redis v9

Rueidis has higher throughput than go-redis v9 across 1, 8, and 64 parallelism settings.

It is even able to achieve ~14x throughput over go-redis in a local benchmark of Macbook Pro 16" M1 Pro 2021. (see parallelism(64)-key(16)-value(64)-10)

Single Client

client_test_set

Cluster Client

cluster_test_set

Benchmark source code: https://github.com/rueian/rueidis-benchmark

There is also a benchmark result performed on two GCP n2-highcpu-2 machines shows that rueidis can achieve higher throughput with lower latencies: https://github.com/rueian/rueidis/pull/93

Client Side Caching

The Opt-In mode of server-assisted client side caching is enabled by default, and can be used by calling DoCache() or DoMultiCache() with an explicit client side TTL.

c.DoCache(ctx, c.B().Hmget().Key("myhash").Field("1", "2").Cache(), time.Minute).ToArray()
c.DoMultiCache(ctx,
    rueidis.CT(c.B().Get().Key("k1").Cache(), 1*time.Minute),
    rueidis.CT(c.B().Get().Key("k2").Cache(), 2*time.Minute))

An explicit client side TTL is required because redis server may not send invalidation message in time when a key is expired on the server. Please follow #6833 and #6867

Although an explicit client side TTL is required, the DoCache() and DoMultiCache() still sends a PTTL command to server and make sure that the client side TTL is not longer than the TTL on server side.

Users can use IsCacheHit() to verify that if the response came from the client side memory.

c.DoCache(ctx, c.B().Get().Key("k1").Cache(), time.Minute).IsCacheHit() == true

If the OpenTelemetry is enabled by the rueidisotel.WithClient(client), then there are also two metrics instrumented:

  • rueidis_do_cache_miss
  • rueidis_do_cache_hits
Benchmark

client_test_get

Benchmark source code: https://github.com/rueian/rueidis-benchmark

Supported Commands by Client Side Caching
  • bitcount
  • bitfieldro
  • bitpos
  • expiretime
  • geodist
  • geohash
  • geopos
  • georadiusro
  • georadiusbymemberro
  • geosearch
  • get
  • mget
  • getbit
  • getrange
  • hexists
  • hget
  • hgetall
  • hkeys
  • hlen
  • hmget
  • hstrlen
  • hvals
  • lindex
  • llen
  • lpos
  • lrange
  • pexpiretime
  • pttl
  • scard
  • sismember
  • smembers
  • smismember
  • sortro
  • strlen
  • ttl
  • type
  • zcard
  • zcount
  • zlexcount
  • zmscore
  • zrange
  • zrangebylex
  • zrangebyscore
  • zrank
  • zrevrange
  • zrevrangebylex
  • zrevrangebyscore
  • zrevrank
  • zscore
  • jsonget
  • jsonmget
  • jsonstrlen
  • jsonarrindex
  • jsonarrlen
  • jsonobjkeys
  • jsonobjlen
  • jsontype
  • jsonresp
  • bfexists
  • bfinfo
  • cfexists
  • cfcount
  • cfinfo
  • cmsquery
  • cmsinfo
  • topkquery
  • topklist
  • topkinfo
  • aitensorget
  • aimodelget
  • aimodelexecute
  • aiscriptget
MGET/JSON.MGET Client Side Caching Helpers

rueidis.MGetCache and rueidis.JsonMGetCache are handy helpers fetching multiple keys across different slots through the client side caching. They will first group keys by slot to build MGET or JSON.MGET commands respectively and then send requests with only cache missed keys to redis nodes.

Broadcast Mode Client Side Caching

Although the default is opt-in mode, you can use broadcast mode by specifying your prefixes in ClientOption.ClientTrackingOptions:

c, err := rueidis.NewClient(rueidis.ClientOption{
	InitAddress:           []string{"127.0.0.1:6379"},
	ClientTrackingOptions: []string{"PREFIX", "prefix1:", "PREFIX", "prefix2:", "BCAST"},
})
if err != nil {
	panic(err)
}
c.DoCache(ctx, c.B().Get().Key("prefix1:1").Cache(), time.Minute).IsCacheHit() == false
c.DoCache(ctx, c.B().Get().Key("prefix1:1").Cache(), time.Minute).IsCacheHit() == true

Please make sure that commands passed to DoCache() and DoMultiCache() are covered by your prefixes. Otherwise, their client-side cache will not be invalidated by redis.

Disable Client Side Caching

Some Redis provider doesn't support client-side caching, ex. Google Cloud Memorystore. You can disable client-side caching by setting ClientOption.DisableCache to true. This will also fall back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti.

Blocking Commands

The following blocking commands use another connection pool and will not share the same connection with non-blocking commands and thus will not cause the pipeline to be blocked:

  • xread with block
  • xreadgroup with block
  • blpop
  • brpop
  • brpoplpush
  • blmove
  • blmpop
  • bzpopmin
  • bzpopmax
  • bzmpop
  • clientpause
  • migrate
  • wait

Context Cancellation

Client.Do(), Client.DoMulti(), Client.DoCache() and Client.DoMultiCache() can return early if the context is canceled or the deadline is reached.

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
c.Do(ctx, c.B().Set().Key("key").Value("val").Nx().Build()).Error() == context.DeadlineExceeded

Please note that though operations can return early, the command is likely sent already.

Pub/Sub

To receive messages from channels, Client.Receive() should be used. It supports SUBSCRIBE, PSUBSCRIBE and Redis 7.0's SSUBSCRIBE:

err = c.Receive(context.Background(), c.B().Subscribe().Channel("ch1", "ch2").Build(), func(msg rueidis.PubSubMessage) {
    // handle the msg
})

The provided handler will be called with received message.

It is important to note that Client.Receive() will keep blocking and return only when the following cases:

  1. return nil when received any unsubscribe/punsubscribe message related to the provided subscribe command.
  2. return rueidis.ErrClosing when the client is closed manually.
  3. return ctx.Err() when the ctx is done.
  4. return non-nil err when the provided subscribe command failed.

While the Client.Receive() call is blocking, the Client is still able to accept other concurrent requests, and they are sharing the same tcp connection. If your message handler may take some time to complete, it is recommended to use the Client.Receive() inside a Client.Dedicated() for not blocking other concurrent requests.

Alternative PubSub Hooks

The Client.Receive() requires users to provide a subscription command in advance. There is an alternative DedicatedClient.SetPubSubHooks() allows users to subscribe/unsubscribe channels later.

client, cancel := c.Dedicate()
defer cancel()

wait := client.SetPubSubHooks(rueidis.PubSubHooks{
	OnMessage: func(m rueidis.PubSubMessage) {
		// Handle message. This callback will be called sequentially, but in another goroutine.
	}
})
client.Do(ctx, client.B().Subscribe().Channel("ch").Build())
err := <-wait // disconnected with err

If the hooks are not nil, the above wait channel is guaranteed to be close when the hooks will not be called anymore, and produce at most one error describing the reason. Users can use this channel to detect disconnection.

CAS Pattern

To do a CAS operation (WATCH + MULTI + EXEC), a dedicated connection should be used, because there should be no unintentional write commands between WATCH and EXEC. Otherwise, the EXEC may not fail as expected.

The dedicated connection shares the same connection pool with blocking commands.

c.Dedicated(func(client client.DedicatedClient) error {
    // watch keys first
    client.Do(ctx, client.B().Watch().Key("k1", "k2").Build())
    // perform read here
    client.Do(ctx, client.B().Mget().Key("k1", "k2").Build())
    // perform write with MULTI EXEC
    client.DoMulti(
        ctx,
        client.B().Multi().Build(),
        client.B().Set().Key("k1").Value("1").Build(),
        client.B().Set().Key("k2").Value("2").Build(),
        client.B().Exec().Build(),
    )
    return nil
})

Or use Dedicate and invoke cancel() when finished to put the connection back to the pool.

client, cancel := c.Dedicate()
defer cancel()

// watch keys first
client.Do(ctx, client.B().Watch().Key("k1", "k2").Build())
// perform read here
client.Do(ctx, client.B().Mget().Key("k1", "k2").Build())
// perform write with MULTI EXEC
client.DoMulti(
    ctx,
    client.B().Multi().Build(),
    client.B().Set().Key("k1").Value("1").Build(),
    client.B().Set().Key("k2").Value("2").Build(),
    client.B().Exec().Build(),
)

However, occupying a connection is not good in terms of throughput. It is better to use Lua script to perform optimistic locking instead.

Memory Consumption Consideration

Each underlying connection in rueidis allocates a ring buffer for pipelining. Its size is controlled by the ClientOption.RingScaleEachConn and the default value is 10 which results into each ring of size 2^10.

If you have many rueidis connections, you may find that they occupy quite amount of memory. In that case, you may consider reducing ClientOption.RingScaleEachConn to 8 or 9 at the cost of potential throughput degradation.

Bulk Operations

The rueidis.Commands and DoMulti() can also be used for bulk operations:

cmds := make(rueidis.Commands, 0, 10)
for i := 0; i < 10; i++ {
    cmds = append(cmds, c.B().Set().Key(strconv.Itoa(i)).Value(strconv.Itoa(i)).Build())
}
for _, resp := range c.DoMulti(ctx, cmds...) {
    if err := resp.Error(); err != nil {
        panic(err)
    }
}

Lua Script

The NewLuaScript or NewLuaScriptReadOnly will create a script which is safe for concurrent usage.

When calling the script.Exec, it will try sending EVALSHA to the client and if the server returns NOSCRIPT, it will send EVAL to try again.

script := rueidis.NewLuaScript("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}")
// the script.Exec is safe for concurrent call
list, err := script.Exec(ctx, client, []string{"k1", "k2"}, []string{"a1", "a2"}).ToArray()

Redis Cluster, Single Redis and Sentinel

To connect to a redis cluster, the NewClient should be used:

c, err := rueidis.NewClient(rueidis.ClientOption{
    InitAddress: []string{"127.0.0.1:7001", "127.0.0.1:7002", "127.0.0.1:7003"},
    ShuffleInit: true,
})

To connect to a single redis node, still use the NewClient with one InitAddress

c, err := rueidis.NewClient(rueidis.ClientOption{
    InitAddress: []string{"127.0.0.1:6379"},
})

To connect to sentinels, specify the required master set name:

c, err := rueidis.NewClient(rueidis.ClientOption{
    InitAddress: []string{"127.0.0.1:26379", "127.0.0.1:26380", "127.0.0.1:26381"},
    Sentinel: rueidis.SentinelOption{
        MasterSet: "my_master",
    },
})

Command Builder

Redis commands are very complex and their formats are very different from each other.

This library provides a type safe command builder within client.B() that can be used as an entrypoint to construct a redis command. Once the command is completed, call the Build() or Cache() to get the actual command. And then pass it to either Client.Do() or Client.DoCache().

c.Do(ctx, c.B().Set().Key("mykey").Value("myval").Ex(10).Nx().Build())

Once the command is passed to the Client.Do(), Client.DoCache(), the command will be recycled and SHOULD NOT be reused.

The ClusterClient.B() also checks if the command contains multiple keys belongs to different slots. If it does, then panic.

Arbitrary command

If you want to construct commands that are not yet supported, you can use c.B().Arbitrary():

// This will result into [ANY CMD k1 k2 a1 a2]
c.B().Arbitrary("ANY", "CMD").Keys("k1", "k2").Args("a1", "a2").Build()
Working with JSON string and []byte

The command builder treats all the parameters as Redis strings, which are binary safe. This means that users can store []byte directly into Redis without conversion. And the rueidis.BinaryString helper can convert []byte to string without copy. For example:

client.B().Set().Key("b").Value(rueidis.BinaryString([]byte{...})).Build()

Treating all the parameters as Redis strings also means that the command builder doesn't do any quoting, conversion automatically for users.

When working with RedisJSON, users frequently need to prepare JSON string in Redis string. And rueidis.JSON can help:

client.B().JsonSet().Key("j").Path("$.myStrField").Value(rueidis.JSON("str")).Build()
// equivalent to
client.B().JsonSet().Key("j").Path("$.myStrField").Value(`"str"`).Build()

High level go-redis like API

Though it is easier to know what command will be sent to redis at first glance if the command is constructed by the command builder, users may sometimes feel it too verbose to write.

For users who don't like the command builder, rueidiscompat.Adapter, contributed mainly by @418Coffee, is an alternative. It is a high level API which is close to go-redis's Cmdable interface.

Migrating from go-redis

You can also try adapting rueidis with existing go-redis code by replacing go-redis's UniversalClient with rueidiscompat.Adapter.

Client side caching

To use client side caching with rueidiscompat.Adapter, chain Cache(ttl) call in front of supported command.

package main

import (
	"context"
	"time"
	"github.com/rueian/rueidis"
	"github.com/rueian/rueidis/rueidiscompat"
)

func main() {
	ctx := context.Background()
	client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
	if err != nil {
		panic(err)
	}
	defer client.Close()

	compat := rueidiscompat.NewAdapter(client)
	ok, _ := compat.SetNX(ctx, "key", "val", time.Second).Result()

	// with client side caching
	res, _ := compat.Cache(time.Second).Get(ctx, "key").Result()
}

Generic Object Mapping

The NewHashRepository and NewJSONRepository creates an OM repository backed by redis hash or RedisJSON.

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/rueian/rueidis"
    "github.com/rueian/rueidis/om"
)

type Example struct {
    Key string `json:"key" redis:",key"` // the redis:",key" is required to indicate which field is the ULID key
    Ver int64  `json:"ver" redis:",ver"` // the redis:",ver" is required to do optimistic locking to prevent lost update
    Str string `json:"myStr"`            // both NewHashRepository and NewJSONRepository use json tag as field name
}

func main() {
    ctx := context.Background()
    c, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
    if err != nil {
        panic(err)
    }
    // create the repo with NewHashRepository or NewJSONRepository
    repo := om.NewHashRepository("my_prefix", Example{}, c)

    exp := repo.NewEntity()
    exp.Str = "mystr"
    fmt.Println(exp.Key) // output 01FNH4FCXV9JTB9WTVFAAKGSYB
    repo.Save(ctx, exp) // success

    // lookup "my_prefix:01FNH4FCXV9JTB9WTVFAAKGSYB" through client side caching
    exp2, _ := repo.FetchCache(ctx, exp.Key, time.Second*5)
    fmt.Println(exp2.Str) // output "mystr", which equals to exp.Str

    exp2.Ver = 0         // if someone changes the version during your GET then SET operation,
    repo.Save(ctx, exp2) // the save will fail with ErrVersionMismatch.
}

Object Mapping + RediSearch

If you have RediSearch, you can create and search the repository against the index.


if _, ok := repo.(*om.HashRepository[Example]); ok {
    repo.CreateIndex(ctx, func(schema om.FtCreateSchema) om.Completed {
        return schema.FieldName("myStr").Text().Build() // Note that the Example.Str field is mapped to myStr on redis by its json tag
    })
}

if _, ok := repo.(*om.JSONRepository[Example]); ok {
    repo.CreateIndex(ctx, func(schema om.FtCreateSchema) om.Completed {
        return schema.FieldName("$.myStr").Text().Build() // the field name of json index should be a json path syntax
    })
}

exp := repo.NewEntity()
exp.Str = "foo"
repo.Save(ctx, exp)

n, records, _ := repo.Search(ctx, func(search om.FtSearchIndex) om.Completed {
    return search.Query("foo").Build() // you have full query capability by building the command from om.FtSearchIndex
})

fmt.Println("total", n) // n is total number of results matched in redis, which is >= len(records)

for _, v := range records {
    fmt.Println(v.Str) // print "foo"
}
Object Mapping Limitation

NewHashRepository only accepts these field types:

  • string, *string
  • int64, *int64
  • bool, *bool
  • []byte

Field projection by RediSearch is not supported.

OpenTelemetry Tracing

Use rueidisotel.WithClient to create a client with OpenTelemetry Tracing enabled.

package main

import (
    "github.com/rueian/rueidis"
    "github.com/rueian/rueidis/rueidisotel"
)

func main() {
    client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
    if err != nil {
        panic(err)
    }
    client = rueidisotel.WithClient(client)
    defer client.Close()
}

Hooks and Other Observability Integration

In addition to rueidisotel, rueidishook provides a general hook mechanism for users to intercept rueidis.Client interface.

See rueidishook for more details.

Distributed Locks with client side caching

See rueidislock for more details.

Writing tests by mocking rueidis

See mock for more details.

Command Response Cheatsheet

It is hard to remember what message type is returned from redis and which parsing method should be used with. So, here is some common examples:

// GET
client.Do(ctx, client.B().Get().Key("k").Build()).ToString()
client.Do(ctx, client.B().Get().Key("k").Build()).AsInt64()
// MGET
client.Do(ctx, client.B().Mget().Key("k1", "k2").Build()).ToArray()
// SET
client.Do(ctx, client.B().Set().Key("k").Value("v").Build()).Error()
// INCR
client.Do(ctx, client.B().Incr().Key("k").Build()).AsInt64()
// HGET
client.Do(ctx, client.B().Hget().Key("k").Field("f").Build()).ToString()
// HMGET
client.Do(ctx, client.B().Hmget().Key("h").Field("a", "b").Build()).ToArray()
// HGETALL
client.Do(ctx, client.B().Hgetall().Key("h").Build()).AsStrMap()
// ZRANGE
client.Do(ctx, client.B().Zrange().Key("k").Min("1").Max("2").Build()).AsStrSlice()
// ZRANK
client.Do(ctx, client.B().Zrank().Key("k").Member("m").Build()).AsInt64()
// ZSCORE
client.Do(ctx, client.B().Zscore().Key("k").Member("m").Build()).AsFloat64()
// ZRANGE
client.Do(ctx, client.B().Zrange().Key("k").Min("0").Max("-1").Build()).AsStrSlice()
client.Do(ctx, client.B().Zrange().Key("k").Min("0").Max("-1").Withscores().Build()).AsZScores()
// ZPOPMIN
client.Do(ctx, client.B().Zpopmin().Key("k").Build()).AsZScore()
client.Do(ctx, client.B().Zpopmin().Key("myzset").Count(2).Build()).AsZScores()
// SCARD
client.Do(ctx, client.B().Scard().Key("k").Build()).AsInt64()
// SMEMBERS
client.Do(ctx, client.B().Smembers().Key("k").Build()).AsStrSlice()
// LINDEX
client.Do(ctx, client.B().Lindex().Key("k").Index(0).Build()).ToString()
// LPOP
client.Do(ctx, client.B().Lpop().Key("k").Build()).ToString()
client.Do(ctx, client.B().Lpop().Key("k").Count(2).Build()).AsStrSlice()

Documentation

Overview

Package rueidis is a fast Golang Redis RESP3 client that does auto pipelining and supports client side caching.

Index

Examples

Constants

View Source
const (
	// DefaultCacheBytes is the default value of ClientOption.CacheSizeEachConn, which is 128 MiB
	DefaultCacheBytes = 128 * (1 << 20)
	// DefaultRingScale is the default value of ClientOption.RingScaleEachConn, which results into having a ring of size 2^10 for each connection
	DefaultRingScale = 10
	// DefaultPoolSize is the default value of ClientOption.BlockingPoolSize
	DefaultPoolSize = 1000
	// DefaultDialTimeout is the default value of ClientOption.Dialer.Timeout
	DefaultDialTimeout = 5 * time.Second
	// DefaultTCPKeepAlive is the default value of ClientOption.Dialer.KeepAlive
	DefaultTCPKeepAlive = 1 * time.Second
	// DefaultReadBuffer is the default value of bufio.NewReaderSize for each connection, which is 0.5MiB
	DefaultReadBuffer = 1 << 19
	// DefaultWriteBuffer is the default value of bufio.NewWriterSize for each connection, which is 0.5MiB
	DefaultWriteBuffer = 1 << 19
)

Variables

View Source
var (
	// ErrClosing means the Client.Close had been called
	ErrClosing = errors.New("rueidis client is closing or unable to connect redis")
	// ErrNoAddr means the ClientOption.InitAddress is empty
	ErrNoAddr = errors.New("no alive address in InitAddress")
	// ErrNoCache means your redis does not support client-side caching and must set ClientOption.DisableCache to true
	ErrNoCache = errors.New("ClientOption.DisableCache must be true for redis not supporting client-side caching or not supporting RESP3")
	// ErrRESP2PubSubMixed means your redis does not support RESP3 and rueidis can't handle SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE in mixed case
	ErrRESP2PubSubMixed = errors.New("rueidis does not support SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE mixed with other commands in RESP2")
)
View Source
var ErrNoSlot = errors.New("the slot has no redis node")

ErrNoSlot indicates that there is no redis node owns the key slot.

Functions

func BinaryString

func BinaryString(bs []byte) string

BinaryString convert the provided []byte into a string without copy. It does what strings.Builder.String() does. Redis Strings are binary safe, this means that it is safe to store any []byte into Redis directly. Users can use this BinaryString helper to insert a []byte as the part of redis command. For example:

client.B().Set().Key(rueidis.BinaryString([]byte{0})).Value(rueidis.BinaryString([]byte{0})).Build()

To read back the []byte of the string returned from the Redis, it is recommended to use the RedisMessage.AsReader.

func IsRedisNil

func IsRedisNil(err error) bool

IsRedisNil is a handy method to check if error is redis nil response. All redis nil response returns as an error.

Example
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

_, err = client.Do(context.Background(), client.B().Get().Key("not_exists").Build()).ToString()
if err != nil && IsRedisNil(err) {
	fmt.Printf("it is a nil response")
}
Output:

func JSON

func JSON(in any) string

JSON convert the provided parameter into a JSON string. Users can use this JSON helper to work with RedisJSON commands. For example:

client.B().JsonSet().Key("a").Path("$.myField").Value(rueidis.JSON("str")).Build()

func JsonMGetCache

func JsonMGetCache(client Client, ctx context.Context, ttl time.Duration, keys []string, path string) (ret map[string]RedisMessage, err error)

JsonMGetCache is a helper that consults the client-side caches with multiple keys by grouping keys within same slot into JSON.MGETs

func MGetCache

func MGetCache(client Client, ctx context.Context, ttl time.Duration, keys []string) (ret map[string]RedisMessage, err error)

MGetCache is a helper that consults the client-side caches with multiple keys by grouping keys within same slot into MGETs

Types

type CacheableTTL

type CacheableTTL struct {
	Cmd cmds.Cacheable
	TTL time.Duration
}

CacheableTTL is parameter container of DoMultiCache

func CT

CT is a shorthand constructor for CacheableTTL

type Client

type Client interface {
	// B is the getter function to the command builder for the client
	// If the client is a cluster client, the command builder also prohibits cross key slots in one command.
	B() cmds.Builder
	// Do is the method sending user's redis command building from the B() to a redis node.
	//  client.Do(ctx, client.B().Get().Key("k").Build()).ToString()
	// All concurrent non-blocking commands will be pipelined automatically and have better throughput.
	// Blocking commands will use another separated connection pool.
	// The cmd parameter is recycled after passing into Do() and should not be reused.
	Do(ctx context.Context, cmd cmds.Completed) (resp RedisResult)
	// DoMulti takes multiple redis commands and sends them together, reducing RTT from the user code.
	// The multi parameters are recycled after passing into DoMulti() and should not be reused.
	DoMulti(ctx context.Context, multi ...cmds.Completed) (resp []RedisResult)
	// DoCache is similar to Do, but it uses opt-in client side caching and requires a client side TTL.
	// The explicit client side TTL specifies the maximum TTL on the client side.
	// If the key's TTL on the server is smaller than the client side TTL, the client side TTL will be capped.
	//  client.Do(ctx, client.B().Get().Key("k").Cache(), time.Minute).ToString()
	// The above example will send the following command to redis if cache miss:
	//  CLIENT CACHING YES
	//  PTTL k
	//  GET k
	// The in-memory cache size is configured by ClientOption.CacheSizeEachConn.
	// The cmd parameter is recycled after passing into DoCache() and should not be reused.
	DoCache(ctx context.Context, cmd cmds.Cacheable, ttl time.Duration) (resp RedisResult)
	// DoMultiCache is similar to DoCache, but works with multiple cacheable commands across different slots.
	// It will first group commands by slots and will send only cache missed commands to redis.
	DoMultiCache(ctx context.Context, multi ...CacheableTTL) (resp []RedisResult)

	// Receive accepts SUBSCRIBE, SSUBSCRIBE, PSUBSCRIBE command and a message handler.
	// Receive will block and then return value only when the following cases:
	//   1. return nil when received any unsubscribe/punsubscribe message related to the provided `subscribe` command.
	//   2. return ErrClosing when the client is closed manually.
	//   3. return ctx.Err() when the `ctx` is done.
	//   4. return non-nil err when the provided `subscribe` command failed.
	Receive(ctx context.Context, subscribe cmds.Completed, fn func(msg PubSubMessage)) error

	// Dedicated acquire a connection from the blocking connection pool, no one else can use the connection
	// during Dedicated. The main usage of Dedicated is CAS operation, which is WATCH + MULTI + EXEC.
	// However, one should try to avoid CAS operation but use Lua script instead, because occupying a connection
	// is not good for performance.
	Dedicated(fn func(DedicatedClient) error) (err error)

	// Dedicate does the same as Dedicated, but it exposes DedicatedClient directly
	// and requires user to invoke cancel() manually to put connection back to the pool.
	Dedicate() (client DedicatedClient, cancel func())

	// Nodes returns each redis node this client known as rueidis.Client. This is useful if you want to
	// send commands to some specific redis nodes in the cluster.
	Nodes() map[string]Client

	// Close will make further calls to the client be rejected with ErrClosing,
	// and Close will wait until all pending calls finished.
	Close()
}

Client is the redis client interface for both single redis instance and redis cluster. It should be created from the NewClient()

Example (DedicateCAS)
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

c, cancel := client.Dedicate()
defer cancel()

ctx := context.Background()

// watch keys first
if err := c.Do(ctx, c.B().Watch().Key("k1", "k2").Build()).Error(); err != nil {
	panic(err)
}
// perform read here
values, err := c.Do(ctx, c.B().Mget().Key("k1", "k2").Build()).ToArray()
if err != nil {
	panic(err)
}
v1, _ := values[0].ToString()
v2, _ := values[1].ToString()
// perform write with MULTI EXEC
for _, resp := range c.DoMulti(
	ctx,
	c.B().Multi().Build(),
	c.B().Set().Key("k1").Value(v1+"1").Build(),
	c.B().Set().Key("k2").Value(v2+"2").Build(),
	c.B().Exec().Build(),
) {
	if err := resp.Error(); err != nil {
		panic(err)
	}
}
Output:

Example (DedicatedCAS)
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

ctx := context.Background()

client.Dedicated(func(client DedicatedClient) error {
	// watch keys first
	if err := client.Do(ctx, client.B().Watch().Key("k1", "k2").Build()).Error(); err != nil {
		return err
	}
	// perform read here
	values, err := client.Do(ctx, client.B().Mget().Key("k1", "k2").Build()).ToArray()
	if err != nil {
		return err
	}
	v1, _ := values[0].ToString()
	v2, _ := values[1].ToString()
	// perform write with MULTI EXEC
	for _, resp := range client.DoMulti(
		ctx,
		client.B().Multi().Build(),
		client.B().Set().Key("k1").Value(v1+"1").Build(),
		client.B().Set().Key("k2").Value(v2+"2").Build(),
		client.B().Exec().Build(),
	) {
		if err := resp.Error(); err != nil {
			return err
		}
	}
	return nil
})
Output:

Example (Do)
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

ctx := context.Background()

client.Do(ctx, client.B().Set().Key("k").Value("1").Build()).Error()

client.Do(ctx, client.B().Get().Key("k").Build()).ToString()

client.Do(ctx, client.B().Get().Key("k").Build()).AsInt64()

client.Do(ctx, client.B().Hmget().Key("h").Field("a", "b").Build()).ToArray()

client.Do(ctx, client.B().Scard().Key("s").Build()).ToInt64()

client.Do(ctx, client.B().Smembers().Key("s").Build()).AsStrSlice()
Output:

Example (DoCache)
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

ctx := context.Background()

client.DoCache(ctx, client.B().Get().Key("k").Cache(), time.Minute).ToString()

client.DoCache(ctx, client.B().Get().Key("k").Cache(), time.Minute).AsInt64()

client.DoCache(ctx, client.B().Hmget().Key("h").Field("a", "b").Cache(), time.Minute).ToArray()

client.DoCache(ctx, client.B().Scard().Key("s").Cache(), time.Minute).ToInt64()

client.DoCache(ctx, client.B().Smembers().Key("s").Cache(), time.Minute).AsStrSlice()
Output:

func NewClient

func NewClient(option ClientOption) (client Client, err error)

NewClient uses ClientOption to initialize the Client for both cluster client and single client. It will first try to connect as cluster client. If the len(ClientOption.InitAddress) == 1 and the address does not enable cluster mode, the NewClient() will use single client instead.

Example (Cluster)
client, _ := NewClient(ClientOption{
	InitAddress: []string{"127.0.0.1:7001", "127.0.0.1:7002", "127.0.0.1:7003"},
	ShuffleInit: true,
})
defer client.Close()
Output:

Example (Sentinel)
client, _ := NewClient(ClientOption{
	InitAddress: []string{"127.0.0.1:26379", "127.0.0.1:26380", "127.0.0.1:26381"},
	Sentinel: SentinelOption{
		MasterSet: "my_master",
	},
})
defer client.Close()
Output:

Example (Single)
client, _ := NewClient(ClientOption{
	InitAddress: []string{"127.0.0.1:6379"},
})
defer client.Close()
Output:

type ClientOption

type ClientOption struct {
	// TCP & TLS
	// Dialer can be used to customized how rueidis connect to a redis instance via TCP, including:
	// - Timeout, the default is DefaultDialTimeout
	// - KeepAlive, the default is DefaultTCPKeepAlive
	// The Dialer.KeepAlive interval is used to detect an unresponsive idle tcp connection.
	// OS takes at least (tcp_keepalive_probes+1)*Dialer.KeepAlive time to conclude an idle connection to be unresponsive.
	// For example: DefaultTCPKeepAlive = 1s and the default of tcp_keepalive_probes on Linux is 9.
	// Therefore, it takes at least 10s to kill an idle and unresponsive tcp connection on Linux by default.
	Dialer    net.Dialer
	TLSConfig *tls.Config

	// Sentinel options, including MasterSet and Auth options
	Sentinel SentinelOption

	// Redis AUTH parameters
	Username   string
	Password   string
	ClientName string

	// InitAddress point to redis nodes.
	// Rueidis will connect to them one by one and issue CLUSTER SLOT command to initialize the cluster client until success.
	// If len(InitAddress) == 1 and the address is not running in cluster mode, rueidis will fall back to the single client mode.
	// If ClientOption.Sentinel.MasterSet is set, then InitAddress will be used to connect sentinels
	InitAddress []string

	SelectDB int

	// CacheSizeEachConn is redis client side cache size that bind to each TCP connection to a single redis instance.
	// The default is DefaultCacheBytes.
	CacheSizeEachConn int

	// RingScaleEachConn sets the size of the ring buffer in each connection to (2 ^ RingScaleEachConn).
	// The default is RingScaleEachConn, which results into having a ring of size 2^10 for each connection.
	// Reduce this value can reduce the memory consumption of each connection at the cost of potential throughput degradation.
	// Values smaller than 8 is typically not recommended.
	RingScaleEachConn int

	// ReadBufferEachConn is the size of the bufio.NewReaderSize for each connection, default to DefaultReadBuffer (0.5 MiB).
	ReadBufferEachConn int
	// WriteBufferEachConn is the size of the bufio.NewWriterSize for each connection, default to DefaultWriteBuffer (0.5 MiB).
	WriteBufferEachConn int

	// BlockingPoolSize is the size of the connection pool shared by blocking commands (ex BLPOP, XREAD with BLOCK).
	// The default is DefaultPoolSize.
	BlockingPoolSize int

	// PipelineMultiplex determines how many tcp connections used to pipeline commands to one redis instance.
	// The default for single and sentinel clients is 2, which means 4 connections (2^2).
	// The default for cluster client is 0, which means 1 connection (2^0).
	PipelineMultiplex int

	// ConnWriteTimeout is applied net.Conn.SetWriteDeadline and periodic PING to redis
	// Since the Dialer.KeepAlive will not be triggered if there is data in the outgoing buffer,
	// ConnWriteTimeout should be set in order to detect local congestion or unresponsive redis server.
	// This default is ClientOption.Dialer.KeepAlive * (9+1), where 9 is the default of tcp_keepalive_probes on Linux.
	ConnWriteTimeout time.Duration

	// ShuffleInit is a handy flag that shuffles the InitAddress after passing to the NewClient() if it is true
	ShuffleInit bool
	// DisableRetry disables retrying read-only commands under network errors
	DisableRetry bool
	// DisableCache falls back Client.DoCache/Client.DoMultiCache to Client.Do/Client.DoMulti
	DisableCache bool
	// OnInvalidations is a callback function in case of client-side caching invalidation received.
	// Note that this function must be fast, otherwise other redis messages will be blocked.
	OnInvalidations func([]RedisMessage)
	// ClientTrackingOptions will be appended to CLIENT TRACKING ON command when the connection is established.
	// The default is []string{"OPTIN"}
	ClientTrackingOptions []string
}

ClientOption should be passed to NewClient to construct a Client

type Commands

type Commands []cmds.Completed

Commands is an exported alias to []cmds.Completed. This allows users to store commands for later usage, for example:

c, release := client.Dedicate()
defer release()

cmds := make(rueidis.Commands, 0, 10)
for i := 0; i < 10; i++ {
    cmds = append(cmds, c.B().Set().Key(strconv.Itoa(i)).Value(strconv.Itoa(i)).Build())
}
for _, resp := range c.DoMulti(ctx, cmds...) {
    if err := resp.Error(); err != nil {
    panic(err)
}

However, please know that once commands are processed by the Do() or DoMulti(), they are recycled and should not be reused.

type DedicatedClient

type DedicatedClient interface {
	// B is inherited from the Client
	B() cmds.Builder
	// Do is the same as Client's
	// The cmd parameter is recycled after passing into Do() and should not be reused.
	Do(ctx context.Context, cmd cmds.Completed) (resp RedisResult)
	// DoMulti takes multiple redis commands and sends them together, reducing RTT from the user code.
	// The multi parameters are recycled after passing into DoMulti() and should not be reused.
	DoMulti(ctx context.Context, multi ...cmds.Completed) (resp []RedisResult)
	// Receive is the same as Client's
	Receive(ctx context.Context, subscribe cmds.Completed, fn func(msg PubSubMessage)) error
	// SetPubSubHooks is an alternative way to processing Pub/Sub messages instead of using Receive.
	// SetPubSubHooks is non-blocking and allows users to subscribe/unsubscribe channels later.
	// Note that the hooks will be called sequentially but in another goroutine.
	// The return value will be either:
	//   1. an error channel, if the hooks passed in is not zero, or
	//   2. nil, if the hooks passed in is zero. (used for reset hooks)
	// In the former case, the error channel is guaranteed to be close when the hooks will not be called anymore,
	// and has at most one error describing the reason why the hooks will not be called anymore.
	// Users can use the error channel to detect disconnection.
	SetPubSubHooks(hooks PubSubHooks) <-chan error
	// Close closes the dedicated connection and prevent the connection be put back into the pool.
	Close()
}

DedicatedClient is obtained from Client.Dedicated() and it will be bound to single redis connection and no other commands can be pipelined in to this connection during Client.Dedicated(). If the DedicatedClient is obtained from cluster client, the first command to it must have a Key() to identify the redis node.

type Lua

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

Lua represents a redis lua script. It should be created from the NewLuaScript() or NewLuaScriptReadOnly()

Example (Exec)
client, err := NewClient(ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
	panic(err)
}
defer client.Close()

ctx := context.Background()

script := NewLuaScript("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}")

script.Exec(ctx, client, []string{"k1", "k2"}, []string{"a1", "a2"}).ToArray()
Output:

func NewLuaScript

func NewLuaScript(script string) *Lua

NewLuaScript creates a Lua instance whose Lua.Exec uses EVALSHA and EVAL.

func NewLuaScriptReadOnly

func NewLuaScriptReadOnly(script string) *Lua

NewLuaScriptReadOnly creates a Lua instance whose Lua.Exec uses EVALSHA_RO and EVAL_RO.

func (*Lua) Exec

func (s *Lua) Exec(ctx context.Context, c Client, keys, args []string) (resp RedisResult)

Exec the script to the given Client. It will first try with the EVALSHA/EVALSHA_RO and then EVAL/EVAL_RO if first try failed. Cross slot keys are prohibited if the Client is a cluster client.

func (*Lua) ExecMulti

func (s *Lua) ExecMulti(ctx context.Context, c Client, multi ...LuaExec) (resp []RedisResult)

ExecMulti exec the script multiple times by the provided LuaExec to the given Client. It will first try SCRIPT LOAD the script to all redis nodes and then exec it with the EVALSHA/EVALSHA_RO. Cross slot keys within single LuaExec are prohibited if the Client is a cluster client.

type LuaExec

type LuaExec struct {
	Keys []string
	Args []string
}

LuaExec is a single execution unit of Lua.ExecMulti

type PubSubHooks

type PubSubHooks struct {
	// OnMessage will be called when receiving "message" and "pmessage" event.
	OnMessage func(m PubSubMessage)
	// OnSubscription will be called when receiving "subscribe", "unsubscribe", "psubscribe" and "punsubscribe" event.
	OnSubscription func(s PubSubSubscription)
}

PubSubHooks can be registered into DedicatedClient to process pubsub messages without using Client.Receive

type PubSubMessage

type PubSubMessage struct {
	// Pattern is only available with pmessage.
	Pattern string
	// Channel is the channel the message belongs to
	Channel string
	// Message is the message content
	Message string
}

PubSubMessage represent a pubsub message from redis

type PubSubSubscription

type PubSubSubscription struct {
	// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
	Kind string
	// Channel is the event subject.
	Channel string
	// Count is the current number of subscriptions for connection.
	Count int64
}

PubSubSubscription represent a pubsub "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" event.

type RedirectMode

type RedirectMode int
const (
	RedirectNone RedirectMode = iota
	RedirectMove
	RedirectAsk
	RedirectRetry
)

type RedisError

type RedisError RedisMessage

RedisError is an error response or a nil message from redis instance

func (*RedisError) Error

func (r *RedisError) Error() string

func (*RedisError) IsAsk

func (r *RedisError) IsAsk() (addr string, ok bool)

IsAsk checks if it is a redis ASK message and returns ask address.

func (*RedisError) IsClusterDown

func (r *RedisError) IsClusterDown() bool

IsClusterDown checks if it is a redis CLUSTERDOWN message and returns ask address.

func (*RedisError) IsMoved

func (r *RedisError) IsMoved() (addr string, ok bool)

IsMoved checks if it is a redis MOVED message and returns moved address.

func (*RedisError) IsNil

func (r *RedisError) IsNil() bool

IsNil checks if it is a redis nil message.

func (*RedisError) IsNoScript

func (r *RedisError) IsNoScript() bool

IsNoScript checks if it is a redis NOSCRIPT message.

func (*RedisError) IsTryAgain

func (r *RedisError) IsTryAgain() bool

IsTryAgain checks if it is a redis TRYAGAIN message and returns ask address.

type RedisMessage

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

RedisMessage is a redis response message, it may be a nil response

func (*RedisMessage) AsBool

func (m *RedisMessage) AsBool() (val bool, err error)

AsBool checks if message is non-nil redis response, and parses it as bool

func (*RedisMessage) AsFloat64

func (m *RedisMessage) AsFloat64() (val float64, err error)

AsFloat64 check if message is a redis string response, and parse it as float64

func (*RedisMessage) AsFloatSlice

func (m *RedisMessage) AsFloatSlice() ([]float64, error)

AsFloatSlice check if message is a redis array/set response, and convert to []float64. redis nil element and other non float element will be present as zero.

func (*RedisMessage) AsInt64

func (m *RedisMessage) AsInt64() (val int64, err error)

AsInt64 check if message is a redis string response, and parse it as int64

func (*RedisMessage) AsIntMap

func (m *RedisMessage) AsIntMap() (map[string]int64, error)

AsIntMap check if message is a redis map/array/set response, and convert to map[string]int64. redis nil element and other non integer element will be present as zero.

func (*RedisMessage) AsIntSlice

func (m *RedisMessage) AsIntSlice() ([]int64, error)

AsIntSlice check if message is a redis array/set response, and convert to []int64. redis nil element and other non integer element will be present as zero.

func (*RedisMessage) AsMap

func (m *RedisMessage) AsMap() (map[string]RedisMessage, error)

AsMap check if message is a redis array/set response, and convert to map[string]RedisMessage

func (*RedisMessage) AsReader

func (m *RedisMessage) AsReader() (reader io.Reader, err error)

AsReader check if message is a redis string response and wrap it with the strings.NewReader

func (*RedisMessage) AsStrMap

func (m *RedisMessage) AsStrMap() (map[string]string, error)

AsStrMap check if message is a redis map/array/set response, and convert to map[string]string. redis nil element and other non string element will be present as zero.

func (*RedisMessage) AsStrSlice

func (m *RedisMessage) AsStrSlice() ([]string, error)

AsStrSlice check if message is a redis array/set response, and convert to []string. redis nil element and other non string element will be present as zero.

func (*RedisMessage) AsXRange

func (m *RedisMessage) AsXRange() ([]XRangeEntry, error)

AsXRange check if message is a redis array/set response, and convert to []XRangeEntry

func (*RedisMessage) AsXRangeEntry

func (m *RedisMessage) AsXRangeEntry() (XRangeEntry, error)

AsXRangeEntry check if message is a redis array/set response of length 2, and convert to XRangeEntry

func (*RedisMessage) AsXRead

func (m *RedisMessage) AsXRead() (ret map[string][]XRangeEntry, err error)

AsXRead converts XREAD/XREADGRUOP response to map[string][]XRangeEntry

func (*RedisMessage) AsZScore

func (m *RedisMessage) AsZScore() (s ZScore, err error)

AsZScore converts ZPOPMAX and ZPOPMIN command with count 1 response to a single ZScore

func (*RedisMessage) AsZScores

func (m *RedisMessage) AsZScores() ([]ZScore, error)

AsZScores converts ZRANGE WITHSCROES, ZDIFF WITHSCROES and ZPOPMAX/ZPOPMIN command with count > 1 responses to []ZScore

func (*RedisMessage) DecodeJSON

func (m *RedisMessage) DecodeJSON(v interface{}) (err error)

DecodeJSON check if message is a redis string response and treat it as json, then unmarshal it into provided value

func (*RedisMessage) Error

func (m *RedisMessage) Error() error

Error check if message is a redis error response, including nil response

func (*RedisMessage) IsArray

func (m *RedisMessage) IsArray() bool

IsArray check if message is a redis array response

func (*RedisMessage) IsBool

func (m *RedisMessage) IsBool() bool

IsBool check if message is a redis RESP3 bool response

func (*RedisMessage) IsCacheHit

func (m *RedisMessage) IsCacheHit() bool

IsCacheHit check if message is from client side cache

func (*RedisMessage) IsFloat64

func (m *RedisMessage) IsFloat64() bool

IsFloat64 check if message is a redis RESP3 double response

func (*RedisMessage) IsInt64

func (m *RedisMessage) IsInt64() bool

IsInt64 check if message is a redis RESP3 int response

func (*RedisMessage) IsMap

func (m *RedisMessage) IsMap() bool

IsMap check if message is a redis RESP3 map response

func (*RedisMessage) IsNil

func (m *RedisMessage) IsNil() bool

IsNil check if message is a redis nil response

func (*RedisMessage) IsString

func (m *RedisMessage) IsString() bool

IsString check if message is a redis string response

func (*RedisMessage) ToAny

func (m *RedisMessage) ToAny() (interface{}, error)

ToAny turns message into go interface{} value

func (*RedisMessage) ToArray

func (m *RedisMessage) ToArray() ([]RedisMessage, error)

ToArray check if message is a redis array/set response, and return it

func (*RedisMessage) ToBool

func (m *RedisMessage) ToBool() (val bool, err error)

ToBool check if message is a redis RESP3 bool response, and return it

func (*RedisMessage) ToFloat64

func (m *RedisMessage) ToFloat64() (val float64, err error)

ToFloat64 check if message is a redis RESP3 double response, and return it

func (*RedisMessage) ToInt64

func (m *RedisMessage) ToInt64() (val int64, err error)

ToInt64 check if message is a redis RESP3 int response, and return it

func (*RedisMessage) ToMap

func (m *RedisMessage) ToMap() (map[string]RedisMessage, error)

ToMap check if message is a redis RESP3 map response, and return it

func (*RedisMessage) ToString

func (m *RedisMessage) ToString() (val string, err error)

ToString check if message is a redis string response, and return it

type RedisResult

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

RedisResult is the return struct from Client.Do or Client.DoCache it contains either a redis response or an underlying error (ex. network timeout).

func (RedisResult) AsBool

func (r RedisResult) AsBool() (bool, error)

AsBool delegates to RedisMessage.AsBool

func (RedisResult) AsFloat64

func (r RedisResult) AsFloat64() (float64, error)

AsFloat64 delegates to RedisMessage.AsFloat64

func (RedisResult) AsFloatSlice

func (r RedisResult) AsFloatSlice() ([]float64, error)

AsFloatSlice delegates to RedisMessage.AsFloatSlice

func (RedisResult) AsInt64

func (r RedisResult) AsInt64() (int64, error)

AsInt64 delegates to RedisMessage.AsInt64

func (RedisResult) AsIntMap

func (r RedisResult) AsIntMap() (map[string]int64, error)

AsIntMap delegates to RedisMessage.AsIntMap

func (RedisResult) AsIntSlice

func (r RedisResult) AsIntSlice() ([]int64, error)

AsIntSlice delegates to RedisMessage.AsIntSlice

func (RedisResult) AsMap

func (r RedisResult) AsMap() (map[string]RedisMessage, error)

AsMap delegates to RedisMessage.AsMap

func (RedisResult) AsReader

func (r RedisResult) AsReader() (reader io.Reader, err error)

AsReader delegates to RedisMessage.AsReader

func (RedisResult) AsStrMap

func (r RedisResult) AsStrMap() (map[string]string, error)

AsStrMap delegates to RedisMessage.AsStrMap

func (RedisResult) AsStrSlice

func (r RedisResult) AsStrSlice() ([]string, error)

AsStrSlice delegates to RedisMessage.AsStrSlice

func (RedisResult) AsXRange

func (r RedisResult) AsXRange() ([]XRangeEntry, error)

AsXRange delegates to RedisMessage.AsXRange

func (RedisResult) AsXRangeEntry

func (r RedisResult) AsXRangeEntry() (XRangeEntry, error)

AsXRangeEntry delegates to RedisMessage.AsXRangeEntry

func (RedisResult) AsXRead

func (r RedisResult) AsXRead() (map[string][]XRangeEntry, error)

AsXRead delegates to RedisMessage.AsXRead

func (RedisResult) AsZScore

func (r RedisResult) AsZScore() (ZScore, error)

AsZScore delegates to RedisMessage.AsZScore

func (RedisResult) AsZScores

func (r RedisResult) AsZScores() ([]ZScore, error)

AsZScores delegates to RedisMessage.AsZScores

func (RedisResult) DecodeJSON

func (r RedisResult) DecodeJSON(v interface{}) error

DecodeJSON delegates to RedisMessage.DecodeJSON

func (RedisResult) Error

func (r RedisResult) Error() error

Error returns either underlying error or redis error or nil

func (RedisResult) IsCacheHit

func (r RedisResult) IsCacheHit() bool

IsCacheHit delegates to RedisMessage.IsCacheHit

func (RedisResult) NonRedisError

func (r RedisResult) NonRedisError() error

NonRedisError can be used to check if there is an underlying error (ex. network timeout).

func (RedisResult) RedisError

func (r RedisResult) RedisError() *RedisError

RedisError can be used to check if the redis response is an error message.

func (RedisResult) ToAny

func (r RedisResult) ToAny() (interface{}, error)

ToAny delegates to RedisMessage.ToAny

func (RedisResult) ToArray

func (r RedisResult) ToArray() ([]RedisMessage, error)

ToArray delegates to RedisMessage.ToArray

func (RedisResult) ToBool

func (r RedisResult) ToBool() (bool, error)

ToBool delegates to RedisMessage.ToBool

func (RedisResult) ToFloat64

func (r RedisResult) ToFloat64() (float64, error)

ToFloat64 delegates to RedisMessage.ToFloat64

func (RedisResult) ToInt64

func (r RedisResult) ToInt64() (int64, error)

ToInt64 delegates to RedisMessage.ToInt64

func (RedisResult) ToMap

func (r RedisResult) ToMap() (map[string]RedisMessage, error)

ToMap delegates to RedisMessage.ToMap

func (RedisResult) ToMessage

func (r RedisResult) ToMessage() (RedisMessage, error)

ToMessage retrieves the RedisMessage

func (RedisResult) ToString

func (r RedisResult) ToString() (string, error)

ToString delegates to RedisMessage.ToString

type SentinelOption

type SentinelOption struct {
	// TCP & TLS, same as ClientOption but for connecting sentinel
	Dialer    net.Dialer
	TLSConfig *tls.Config

	// MasterSet is the redis master set name monitored by sentinel cluster.
	// If this field is set, then ClientOption.InitAddress will be used to connect to sentinel cluster.
	MasterSet string

	// Redis AUTH parameters for sentinel
	Username   string
	Password   string
	ClientName string
}

SentinelOption contains MasterSet,

type XRangeEntry

type XRangeEntry struct {
	ID          string
	FieldValues map[string]string
}

XRangeEntry is the element type of both XRANGE and XREVRANGE command response array

type ZScore

type ZScore struct {
	Member string
	Score  float64
}

ZScore is the element type of ZRANGE WITHSCORES, ZDIFF WITHSCORES and ZPOPMAX command response

Directories

Path Synopsis
hack
internal

Jump to

Keyboard shortcuts

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