Upgrade go-redis from v9.3.0 to v9.4.0 (#7593)
Skip v9.3.1 and go straight to v9.4.0 because it's a fix for a
breaking change introduced in v9.3.1. I don't believe we can upgrade to
v9.5.x at this time because of our [redis container
version](fa3b0106e5/docker-compose.yml (L110-L111)).
Changes from
[v9.3.1](https://github.com/redis/go-redis/releases/tag/v9.3.1)
Changes from
[v9.4.0](https://github.com/redis/go-redis/releases/tag/v9.4.0)
This commit is contained in:
parent
472effbb9b
commit
b61c7e1fdd
4
go.mod
4
go.mod
|
|
@ -24,7 +24,7 @@ require (
|
|||
github.com/nxadm/tail v1.4.11
|
||||
github.com/prometheus/client_golang v1.15.1
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
github.com/redis/go-redis/v9 v9.3.0
|
||||
github.com/redis/go-redis/v9 v9.4.0
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399
|
||||
github.com/weppos/publicsuffix-go v0.30.3-0.20240510084413-5f1d03393b3d
|
||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c
|
||||
|
|
@ -63,7 +63,7 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -55,8 +55,8 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
|
|||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
|
|
@ -214,8 +214,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
|
|||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
|
||||
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
||||
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
|
|
|
|||
|
|
@ -70,3 +70,5 @@ benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
|||
- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
|
||||
- [FreeCache](https://github.com/coocood/freecache)
|
||||
- [FastCache](https://github.com/VictoriaMetrics/fastcache)
|
||||
- [Ristretto](https://github.com/dgraph-io/ristretto)
|
||||
- [Badger](https://github.com/dgraph-io/badger)
|
||||
|
|
|
|||
|
|
@ -19,10 +19,13 @@ const (
|
|||
// Store the primes in an array as well.
|
||||
//
|
||||
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||
// contiguous array of the assembly code.
|
||||
// contiguous array for the assembly code.
|
||||
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||
|
||||
// Digest implements hash.Hash64.
|
||||
//
|
||||
// Note that a zero-valued Digest is not ready to receive writes.
|
||||
// Call Reset or create a Digest using New before calling other methods.
|
||||
type Digest struct {
|
||||
v1 uint64
|
||||
v2 uint64
|
||||
|
|
@ -33,19 +36,31 @@ type Digest struct {
|
|||
n int // how much of mem is used
|
||||
}
|
||||
|
||||
// New creates a new Digest that computes the 64-bit xxHash algorithm.
|
||||
// New creates a new Digest with a zero seed.
|
||||
func New() *Digest {
|
||||
return NewWithSeed(0)
|
||||
}
|
||||
|
||||
// NewWithSeed creates a new Digest with the given seed.
|
||||
func NewWithSeed(seed uint64) *Digest {
|
||||
var d Digest
|
||||
d.Reset()
|
||||
d.ResetWithSeed(seed)
|
||||
return &d
|
||||
}
|
||||
|
||||
// Reset clears the Digest's state so that it can be reused.
|
||||
// It uses a seed value of zero.
|
||||
func (d *Digest) Reset() {
|
||||
d.v1 = primes[0] + prime2
|
||||
d.v2 = prime2
|
||||
d.v3 = 0
|
||||
d.v4 = -primes[0]
|
||||
d.ResetWithSeed(0)
|
||||
}
|
||||
|
||||
// ResetWithSeed clears the Digest's state so that it can be reused.
|
||||
// It uses the given seed to initialize the state.
|
||||
func (d *Digest) ResetWithSeed(seed uint64) {
|
||||
d.v1 = seed + prime1 + prime2
|
||||
d.v2 = seed + prime2
|
||||
d.v3 = seed
|
||||
d.v4 = seed - prime1
|
||||
d.total = 0
|
||||
d.n = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
//
|
||||
//go:noescape
|
||||
func Sum64(b []byte) uint64
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
func Sum64(b []byte) uint64 {
|
||||
// A simpler version would be
|
||||
// d := New()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
func Sum64String(s string) uint64 {
|
||||
return Sum64([]byte(s))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import (
|
|||
//
|
||||
// See https://github.com/golang/go/issues/42739 for discussion.
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
|
||||
func Sum64String(s string) uint64 {
|
||||
b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))
|
||||
|
|
|
|||
|
|
@ -149,15 +149,16 @@ import (
|
|||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func ExampleClient() {
|
||||
url := "redis://localhost:6379?password=hello&protocol=3"
|
||||
func ExampleClient() *redis.Client {
|
||||
url := "redis://user:password@localhost:6379/0?protocol=3"
|
||||
opts, err := redis.ParseURL(url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rdb := redis.NewClient(opts)
|
||||
|
||||
return redis.NewClient(opts)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package redis
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type BitMapCmdable interface {
|
||||
GetBit(ctx context.Context, key string, offset int64) *IntCmd
|
||||
|
|
@ -127,3 +129,21 @@ func (c cmdable) BitField(ctx context.Context, key string, values ...interface{}
|
|||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// BitFieldRO - Read-only variant of the BITFIELD command.
|
||||
// It is like the original BITFIELD but only accepts GET subcommand and can safely be used in read-only replicas.
|
||||
// - BitFieldRO(ctx, key, "<Encoding0>", "<Offset0>", "<Encoding1>","<Offset1>")
|
||||
func (c cmdable) BitFieldRO(ctx context.Context, key string, values ...interface{}) *IntSliceCmd {
|
||||
args := make([]interface{}, 2, 2+len(values))
|
||||
args[0] = "BITFIELD_RO"
|
||||
args[1] = key
|
||||
if len(values)%2 != 0 {
|
||||
panic("BitFieldRO: invalid number of arguments, must be even")
|
||||
}
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
args = append(args, "GET", values[i], values[i+1])
|
||||
}
|
||||
cmd := NewIntSliceCmd(ctx, args...)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9/internal"
|
||||
|
|
@ -17,10 +18,22 @@ import (
|
|||
)
|
||||
|
||||
type Cmder interface {
|
||||
// command name.
|
||||
// e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster".
|
||||
Name() string
|
||||
|
||||
// full command name.
|
||||
// e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster info".
|
||||
FullName() string
|
||||
|
||||
// all args of the command.
|
||||
// e.g. "set k v ex 10" -> "[set k v ex 10]".
|
||||
Args() []interface{}
|
||||
|
||||
// format request and response string.
|
||||
// e.g. "set k v ex 10" -> "set k v ex 10: OK", "get k" -> "get k: v".
|
||||
String() string
|
||||
|
||||
stringArg(int) string
|
||||
firstKeyPos() int8
|
||||
SetFirstKeyPos(int8)
|
||||
|
|
@ -62,7 +75,7 @@ func writeCmd(wr *proto.Writer, cmd Cmder) error {
|
|||
return wr.WriteArgs(cmd.Args())
|
||||
}
|
||||
|
||||
func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
|
||||
func cmdFirstKeyPos(cmd Cmder) int {
|
||||
if pos := cmd.firstKeyPos(); pos != 0 {
|
||||
return int(pos)
|
||||
}
|
||||
|
|
@ -82,10 +95,6 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
|
|||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if info != nil {
|
||||
return int(info.FirstKeyPos)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
@ -845,7 +854,7 @@ func (cmd *StringCmd) Val() string {
|
|||
}
|
||||
|
||||
func (cmd *StringCmd) Result() (string, error) {
|
||||
return cmd.Val(), cmd.err
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *StringCmd) Bytes() ([]byte, error) {
|
||||
|
|
@ -949,7 +958,7 @@ func (cmd *FloatCmd) Val() float64 {
|
|||
}
|
||||
|
||||
func (cmd *FloatCmd) Result() (float64, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *FloatCmd) String() string {
|
||||
|
|
@ -1044,7 +1053,7 @@ func (cmd *StringSliceCmd) Val() []string {
|
|||
}
|
||||
|
||||
func (cmd *StringSliceCmd) Result() ([]string, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *StringSliceCmd) String() string {
|
||||
|
|
@ -2706,7 +2715,7 @@ func (cmd *ZWithKeyCmd) Val() *ZWithKey {
|
|||
}
|
||||
|
||||
func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *ZWithKeyCmd) String() string {
|
||||
|
|
@ -2844,7 +2853,7 @@ func (cmd *ClusterSlotsCmd) Val() []ClusterSlot {
|
|||
}
|
||||
|
||||
func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *ClusterSlotsCmd) String() string {
|
||||
|
|
@ -3304,7 +3313,7 @@ func (cmd *GeoPosCmd) Val() []*GeoPos {
|
|||
}
|
||||
|
||||
func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *GeoPosCmd) String() string {
|
||||
|
|
@ -3385,7 +3394,7 @@ func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo {
|
|||
}
|
||||
|
||||
func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *CommandsInfoCmd) String() string {
|
||||
|
|
@ -3575,7 +3584,7 @@ func (cmd *SlowLogCmd) Val() []SlowLog {
|
|||
}
|
||||
|
||||
func (cmd *SlowLogCmd) Result() ([]SlowLog, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *SlowLogCmd) String() string {
|
||||
|
|
@ -3674,7 +3683,7 @@ func (cmd *MapStringInterfaceCmd) Val() map[string]interface{} {
|
|||
}
|
||||
|
||||
func (cmd *MapStringInterfaceCmd) Result() (map[string]interface{}, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *MapStringInterfaceCmd) String() string {
|
||||
|
|
@ -3738,7 +3747,7 @@ func (cmd *MapStringStringSliceCmd) Val() []map[string]string {
|
|||
}
|
||||
|
||||
func (cmd *MapStringStringSliceCmd) Result() ([]map[string]string, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *MapStringStringSliceCmd) String() string {
|
||||
|
|
@ -3802,7 +3811,7 @@ func (cmd *MapStringInterfaceSliceCmd) Val() []map[string]interface{} {
|
|||
}
|
||||
|
||||
func (cmd *MapStringInterfaceSliceCmd) Result() ([]map[string]interface{}, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *MapStringInterfaceSliceCmd) String() string {
|
||||
|
|
@ -4652,7 +4661,7 @@ func (cmd *ClusterLinksCmd) Val() []ClusterLink {
|
|||
}
|
||||
|
||||
func (cmd *ClusterLinksCmd) Result() ([]ClusterLink, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *ClusterLinksCmd) String() string {
|
||||
|
|
@ -4754,7 +4763,7 @@ func (cmd *ClusterShardsCmd) Val() []ClusterShard {
|
|||
}
|
||||
|
||||
func (cmd *ClusterShardsCmd) Result() ([]ClusterShard, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *ClusterShardsCmd) String() string {
|
||||
|
|
@ -5227,7 +5236,7 @@ func (cmd *ACLLogCmd) Val() []*ACLLogEntry {
|
|||
}
|
||||
|
||||
func (cmd *ACLLogCmd) Result() ([]*ACLLogEntry, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *ACLLogCmd) String() string {
|
||||
|
|
@ -5328,7 +5337,7 @@ func (cmd *InfoCmd) Val() map[string]map[string]string {
|
|||
}
|
||||
|
||||
func (cmd *InfoCmd) Result() (map[string]map[string]string, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *InfoCmd) String() string {
|
||||
|
|
@ -5369,7 +5378,6 @@ func (cmd *InfoCmd) readReply(rd *proto.Reader) error {
|
|||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (cmd *InfoCmd) Item(section, key string) string {
|
||||
|
|
@ -5381,3 +5389,85 @@ func (cmd *InfoCmd) Item(section, key string) string {
|
|||
return cmd.val[section][key]
|
||||
}
|
||||
}
|
||||
|
||||
type MonitorStatus int
|
||||
|
||||
const (
|
||||
monitorStatusIdle MonitorStatus = iota
|
||||
monitorStatusStart
|
||||
monitorStatusStop
|
||||
)
|
||||
|
||||
type MonitorCmd struct {
|
||||
baseCmd
|
||||
ch chan string
|
||||
status MonitorStatus
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newMonitorCmd(ctx context.Context, ch chan string) *MonitorCmd {
|
||||
return &MonitorCmd{
|
||||
baseCmd: baseCmd{
|
||||
ctx: ctx,
|
||||
args: []interface{}{"monitor"},
|
||||
},
|
||||
ch: ch,
|
||||
status: monitorStatusIdle,
|
||||
mu: sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *MonitorCmd) String() string {
|
||||
return cmdString(cmd, nil)
|
||||
}
|
||||
|
||||
func (cmd *MonitorCmd) readReply(rd *proto.Reader) error {
|
||||
ctx, cancel := context.WithCancel(cmd.ctx)
|
||||
go func(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
err := cmd.readMonitor(rd, cancel)
|
||||
if err != nil {
|
||||
cmd.err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *MonitorCmd) readMonitor(rd *proto.Reader, cancel context.CancelFunc) error {
|
||||
for {
|
||||
cmd.mu.Lock()
|
||||
st := cmd.status
|
||||
cmd.mu.Unlock()
|
||||
if pk, _ := rd.Peek(1); len(pk) != 0 && st == monitorStatusStart {
|
||||
line, err := rd.ReadString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.ch <- line
|
||||
}
|
||||
if st == monitorStatusStop {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *MonitorCmd) Start() {
|
||||
cmd.mu.Lock()
|
||||
defer cmd.mu.Unlock()
|
||||
cmd.status = monitorStatusStart
|
||||
}
|
||||
|
||||
func (cmd *MonitorCmd) Stop() {
|
||||
cmd.mu.Lock()
|
||||
defer cmd.mu.Unlock()
|
||||
cmd.status = monitorStatusStop
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ type Cmdable interface {
|
|||
SlowLogGet(ctx context.Context, num int64) *SlowLogCmd
|
||||
Time(ctx context.Context) *TimeCmd
|
||||
DebugObject(ctx context.Context, key string) *StringCmd
|
||||
|
||||
MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
|
||||
|
||||
ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
|
||||
|
|
@ -700,3 +699,20 @@ func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *St
|
|||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
/*
|
||||
Monitor - represents a Redis MONITOR command, allowing the user to capture
|
||||
and process all commands sent to a Redis server. This mimics the behavior of
|
||||
MONITOR in the redis-cli.
|
||||
|
||||
Notes:
|
||||
- Using MONITOR blocks the connection to the server for itself. It needs a dedicated connection
|
||||
- The user should create a channel of type string
|
||||
- This runs concurrently in the background. Trigger via the Start and Stop functions
|
||||
See further: Redis MONITOR command: https://redis.io/commands/monitor
|
||||
*/
|
||||
func (c cmdable) Monitor(ctx context.Context, ch chan string) *MonitorCmd {
|
||||
cmd := newMonitorCmd(ctx, ch)
|
||||
_ = c(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,11 @@ func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
|
|||
}
|
||||
|
||||
// Use the built-in decoder.
|
||||
out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
|
||||
kind := f.Type.Kind()
|
||||
if kind == reflect.Pointer {
|
||||
kind = f.Type.Elem().Kind()
|
||||
}
|
||||
out.set(tag, &structField{index: i, fn: decoders[kind]})
|
||||
}
|
||||
|
||||
return out
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ type JSONCmd struct {
|
|||
var _ Cmder = (*JSONCmd)(nil)
|
||||
|
||||
func newJSONCmd(ctx context.Context, args ...interface{}) *JSONCmd {
|
||||
|
||||
return &JSONCmd{
|
||||
baseCmd: baseCmd{
|
||||
ctx: ctx,
|
||||
|
|
@ -95,7 +94,6 @@ func (cmd *JSONCmd) Val() string {
|
|||
} else {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (cmd *JSONCmd) Result() (string, error) {
|
||||
|
|
@ -103,7 +101,6 @@ func (cmd *JSONCmd) Result() (string, error) {
|
|||
}
|
||||
|
||||
func (cmd JSONCmd) Expanded() (interface{}, error) {
|
||||
|
||||
if len(cmd.val) != 0 && cmd.expanded == nil {
|
||||
err := json.Unmarshal([]byte(cmd.val), &cmd.expanded)
|
||||
if err != nil {
|
||||
|
|
@ -115,7 +112,6 @@ func (cmd JSONCmd) Expanded() (interface{}, error) {
|
|||
}
|
||||
|
||||
func (cmd *JSONCmd) readReply(rd *proto.Reader) error {
|
||||
|
||||
// nil response from JSON.(M)GET (cmd.baseCmd.err will be "redis: nil")
|
||||
if cmd.baseCmd.Err() == Nil {
|
||||
cmd.val = ""
|
||||
|
|
@ -131,7 +127,7 @@ func (cmd *JSONCmd) readReply(rd *proto.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var expanded = make([]interface{}, size)
|
||||
expanded := make([]interface{}, size)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
if expanded[i], err = rd.ReadReply(); err != nil {
|
||||
|
|
@ -182,11 +178,10 @@ func (cmd *JSONSliceCmd) Val() []interface{} {
|
|||
}
|
||||
|
||||
func (cmd *JSONSliceCmd) Result() ([]interface{}, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error {
|
||||
|
||||
if cmd.baseCmd.Err() == Nil {
|
||||
cmd.val = nil
|
||||
return Nil
|
||||
|
|
@ -220,7 +215,6 @@ func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error {
|
|||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
|
@ -258,11 +252,10 @@ func (cmd *IntPointerSliceCmd) Val() []*int64 {
|
|||
}
|
||||
|
||||
func (cmd *IntPointerSliceCmd) Result() ([]*int64, error) {
|
||||
return cmd.Val(), cmd.Err()
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *IntPointerSliceCmd) readReply(rd *proto.Reader) error {
|
||||
|
||||
n, err := rd.ReadArrayLen()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ type Options struct {
|
|||
|
||||
// Disable set-lib on connect. Default is false.
|
||||
DisableIndentity bool
|
||||
|
||||
// Add suffix to client name. Default is empty.
|
||||
IdentitySuffix string
|
||||
}
|
||||
|
||||
func (opt *Options) init() {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ type ClusterOptions struct {
|
|||
|
||||
TLSConfig *tls.Config
|
||||
DisableIndentity bool // Disable set-lib on connect. Default is false.
|
||||
|
||||
IdentitySuffix string // Add suffix to client name. Default is empty.
|
||||
}
|
||||
|
||||
func (opt *ClusterOptions) init() {
|
||||
|
|
@ -291,6 +293,7 @@ func (opt *ClusterOptions) clientOptions() *Options {
|
|||
ConnMaxIdleTime: opt.ConnMaxIdleTime,
|
||||
ConnMaxLifetime: opt.ConnMaxLifetime,
|
||||
DisableIndentity: opt.DisableIndentity,
|
||||
IdentitySuffix: opt.IdentitySuffix,
|
||||
TLSConfig: opt.TLSConfig,
|
||||
// If ClusterSlots is populated, then we probably have an artificial
|
||||
// cluster whose nodes are not in clustering mode (otherwise there isn't
|
||||
|
|
@ -907,7 +910,6 @@ func (c *ClusterClient) Process(ctx context.Context, cmd Cmder) error {
|
|||
}
|
||||
|
||||
func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
|
||||
cmdInfo := c.cmdInfo(ctx, cmd.Name())
|
||||
slot := c.cmdSlot(ctx, cmd)
|
||||
var node *clusterNode
|
||||
var ask bool
|
||||
|
|
@ -921,7 +923,7 @@ func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
|
|||
|
||||
if node == nil {
|
||||
var err error
|
||||
node, err = c.cmdNode(ctx, cmdInfo, slot)
|
||||
node, err = c.cmdNode(ctx, cmd.Name(), slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1783,8 +1785,7 @@ func (c *ClusterClient) cmdSlot(ctx context.Context, cmd Cmder) int {
|
|||
return args[2].(int)
|
||||
}
|
||||
|
||||
cmdInfo := c.cmdInfo(ctx, cmd.Name())
|
||||
return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
|
||||
return cmdSlot(cmd, cmdFirstKeyPos(cmd))
|
||||
}
|
||||
|
||||
func cmdSlot(cmd Cmder, pos int) int {
|
||||
|
|
@ -1797,7 +1798,7 @@ func cmdSlot(cmd Cmder, pos int) int {
|
|||
|
||||
func (c *ClusterClient) cmdNode(
|
||||
ctx context.Context,
|
||||
cmdInfo *CommandInfo,
|
||||
cmdName string,
|
||||
slot int,
|
||||
) (*clusterNode, error) {
|
||||
state, err := c.state.Get(ctx)
|
||||
|
|
@ -1805,8 +1806,11 @@ func (c *ClusterClient) cmdNode(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if c.opt.ReadOnly && cmdInfo != nil && cmdInfo.ReadOnly {
|
||||
return c.slotReadOnlyNode(state, slot)
|
||||
if c.opt.ReadOnly {
|
||||
cmdInfo := c.cmdInfo(ctx, cmdName)
|
||||
if cmdInfo != nil && cmdInfo.ReadOnly {
|
||||
return c.slotReadOnlyNode(state, slot)
|
||||
}
|
||||
}
|
||||
return state.slotMasterNode(slot)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "redis",
|
||||
"version": "9.3.0",
|
||||
"version": "9.4.0",
|
||||
"main": "index.js",
|
||||
"repository": "git@github.com:redis/go-redis.git",
|
||||
"author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
|
@ -40,12 +41,15 @@ type (
|
|||
)
|
||||
|
||||
type hooksMixin struct {
|
||||
hooksMu *sync.Mutex
|
||||
|
||||
slice []Hook
|
||||
initial hooks
|
||||
current hooks
|
||||
}
|
||||
|
||||
func (hs *hooksMixin) initHooks(hooks hooks) {
|
||||
hs.hooksMu = new(sync.Mutex)
|
||||
hs.initial = hooks
|
||||
hs.chain()
|
||||
}
|
||||
|
|
@ -116,6 +120,9 @@ func (hs *hooksMixin) AddHook(hook Hook) {
|
|||
func (hs *hooksMixin) chain() {
|
||||
hs.initial.setDefaults()
|
||||
|
||||
hs.hooksMu.Lock()
|
||||
defer hs.hooksMu.Unlock()
|
||||
|
||||
hs.current.dial = hs.initial.dial
|
||||
hs.current.process = hs.initial.process
|
||||
hs.current.pipeline = hs.initial.pipeline
|
||||
|
|
@ -138,9 +145,13 @@ func (hs *hooksMixin) chain() {
|
|||
}
|
||||
|
||||
func (hs *hooksMixin) clone() hooksMixin {
|
||||
hs.hooksMu.Lock()
|
||||
defer hs.hooksMu.Unlock()
|
||||
|
||||
clone := *hs
|
||||
l := len(clone.slice)
|
||||
clone.slice = clone.slice[:l:l]
|
||||
clone.hooksMu = new(sync.Mutex)
|
||||
return clone
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +176,8 @@ func (hs *hooksMixin) withProcessPipelineHook(
|
|||
}
|
||||
|
||||
func (hs *hooksMixin) dialHook(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
hs.hooksMu.Lock()
|
||||
defer hs.hooksMu.Unlock()
|
||||
return hs.current.dial(ctx, network, addr)
|
||||
}
|
||||
|
||||
|
|
@ -302,6 +315,9 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
|||
if !c.opt.DisableIndentity {
|
||||
libName := ""
|
||||
libVer := Version()
|
||||
if c.opt.IdentitySuffix != "" {
|
||||
libName = c.opt.IdentitySuffix
|
||||
}
|
||||
libInfo := LibraryInfo{LibName: &libName}
|
||||
conn.ClientSetInfo(ctx, libInfo)
|
||||
libInfo = LibraryInfo{LibVer: &libVer}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ type RingOptions struct {
|
|||
Limiter Limiter
|
||||
|
||||
DisableIndentity bool
|
||||
IdentitySuffix string
|
||||
}
|
||||
|
||||
func (opt *RingOptions) init() {
|
||||
|
|
@ -166,6 +167,7 @@ func (opt *RingOptions) clientOptions() *Options {
|
|||
Limiter: opt.Limiter,
|
||||
|
||||
DisableIndentity: opt.DisableIndentity,
|
||||
IdentitySuffix: opt.IdentitySuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -678,21 +680,8 @@ func (c *Ring) cmdsInfo(ctx context.Context) (map[string]*CommandInfo, error) {
|
|||
return nil, firstErr
|
||||
}
|
||||
|
||||
func (c *Ring) cmdInfo(ctx context.Context, name string) *CommandInfo {
|
||||
cmdsInfo, err := c.cmdsInfoCache.Get(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
info := cmdsInfo[name]
|
||||
if info == nil {
|
||||
internal.Logger.Printf(ctx, "info for cmd=%s not found", name)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func (c *Ring) cmdShard(ctx context.Context, cmd Cmder) (*ringShard, error) {
|
||||
cmdInfo := c.cmdInfo(ctx, cmd.Name())
|
||||
pos := cmdFirstKeyPos(cmd, cmdInfo)
|
||||
pos := cmdFirstKeyPos(cmd)
|
||||
if pos == 0 {
|
||||
return c.sharding.Random()
|
||||
}
|
||||
|
|
@ -760,8 +749,7 @@ func (c *Ring) generalProcessPipeline(
|
|||
cmdsMap := make(map[string][]Cmder)
|
||||
|
||||
for _, cmd := range cmds {
|
||||
cmdInfo := c.cmdInfo(ctx, cmd.Name())
|
||||
hash := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
|
||||
hash := cmd.stringArg(cmdFirstKeyPos(cmd))
|
||||
if hash != "" {
|
||||
hash = c.sharding.Hash(hash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ type FailoverOptions struct {
|
|||
TLSConfig *tls.Config
|
||||
|
||||
DisableIndentity bool
|
||||
IdentitySuffix string
|
||||
}
|
||||
|
||||
func (opt *FailoverOptions) clientOptions() *Options {
|
||||
|
|
@ -117,6 +118,7 @@ func (opt *FailoverOptions) clientOptions() *Options {
|
|||
TLSConfig: opt.TLSConfig,
|
||||
|
||||
DisableIndentity: opt.DisableIndentity,
|
||||
IdentitySuffix: opt.IdentitySuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -531,6 +531,8 @@ func (c cmdable) TSInfoWithArgs(ctx context.Context, key string, options *TSInfo
|
|||
}
|
||||
|
||||
// TSMAdd - Adds multiple samples to multiple time-series keys.
|
||||
// It accepts a slice of 'ktv' slices, each containing exactly three elements: key, timestamp, and value.
|
||||
// This struct must be provided for this command to work.
|
||||
// For more information - https://redis.io/commands/ts.madd/
|
||||
func (c cmdable) TSMAdd(ctx context.Context, ktvSlices [][]interface{}) *IntSliceCmd {
|
||||
args := []interface{}{"TS.MADD"}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ type UniversalOptions struct {
|
|||
MasterName string
|
||||
|
||||
DisableIndentity bool
|
||||
IdentitySuffix string
|
||||
}
|
||||
|
||||
// Cluster returns cluster options created from the universal options.
|
||||
|
|
@ -112,6 +113,7 @@ func (o *UniversalOptions) Cluster() *ClusterOptions {
|
|||
TLSConfig: o.TLSConfig,
|
||||
|
||||
DisableIndentity: o.DisableIndentity,
|
||||
IdentitySuffix: o.IdentitySuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +159,7 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
|
|||
TLSConfig: o.TLSConfig,
|
||||
|
||||
DisableIndentity: o.DisableIndentity,
|
||||
IdentitySuffix: o.IdentitySuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +202,7 @@ func (o *UniversalOptions) Simple() *Options {
|
|||
TLSConfig: o.TLSConfig,
|
||||
|
||||
DisableIndentity: o.DisableIndentity,
|
||||
IdentitySuffix: o.IdentitySuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@ package redis
|
|||
|
||||
// Version is the current release version.
|
||||
func Version() string {
|
||||
return "9.3.0"
|
||||
return "9.4.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ github.com/beorn7/perks/quantile
|
|||
# github.com/cenkalti/backoff/v4 v4.3.0
|
||||
## explicit; go 1.18
|
||||
github.com/cenkalti/backoff/v4
|
||||
# github.com/cespare/xxhash/v2 v2.2.0
|
||||
# github.com/cespare/xxhash/v2 v2.3.0
|
||||
## explicit; go 1.11
|
||||
github.com/cespare/xxhash/v2
|
||||
# github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
|
||||
|
|
@ -249,7 +249,7 @@ github.com/prometheus/common/model
|
|||
github.com/prometheus/procfs
|
||||
github.com/prometheus/procfs/internal/fs
|
||||
github.com/prometheus/procfs/internal/util
|
||||
# github.com/redis/go-redis/v9 v9.3.0
|
||||
# github.com/redis/go-redis/v9 v9.4.0
|
||||
## explicit; go 1.18
|
||||
github.com/redis/go-redis/v9
|
||||
github.com/redis/go-redis/v9/internal
|
||||
|
|
|
|||
Loading…
Reference in New Issue