diff --git a/backend/go.mod b/backend/go.mod index fc6a35e..32295fd 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 - github.com/redis/go-redis/v9 v9.2.1 + github.com/redis/go-redis/v9 v9.4.0 github.com/spf13/viper v1.17.0 ) @@ -41,7 +41,7 @@ require ( github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.5 github.com/subosito/gotenv v1.6.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index c3035a8..7ccde59 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -182,8 +182,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps= -github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= -github.com/redis/go-redis/v9 v9.2.1/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/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= diff --git a/backend/vendor/github.com/redis/go-redis/v9/README.md b/backend/vendor/github.com/redis/go-redis/v9/README.md index 18095ee..98edbe2 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/README.md +++ b/backend/vendor/github.com/redis/go-redis/v9/README.md @@ -10,8 +10,22 @@ > use it to monitor applications and set up automatic alerts to receive notifications via email, > Slack, Telegram, and others. > -> See [OpenTelemetry](example/otel) example which demonstrates how you can use Uptrace to monitor -> go-redis. +> See [OpenTelemetry](https://github.com/redis/go-redis/tree/master/example/otel) example which +> demonstrates how you can use Uptrace to monitor go-redis. + +## How do I Redis? + +[Learn for free at Redis University](https://university.redis.com/) + +[Build faster with the Redis Launchpad](https://launchpad.redis.com/) + +[Try the Redis Cloud](https://redis.com/try-free/) + +[Dive in developer tutorials](https://developer.redis.com/) + +[Join the Redis community](https://redis.com/community/) + +[Work at Redis](https://redis.com/company/careers/jobs/) ## Documentation @@ -107,7 +121,8 @@ func ExampleClient() { } ``` -The above can be modified to specify the version of the RESP protocol by adding the `protocol` option to the `Options` struct: +The above can be modified to specify the version of the RESP protocol by adding the `protocol` +option to the `Options` struct: ```go rdb := redis.NewClient(&redis.Options{ @@ -121,7 +136,10 @@ The above can be modified to specify the version of the RESP protocol by adding ### Connecting via a redis url -go-redis also supports connecting via the [redis uri specification](https://github.com/redis/redis-specifications/tree/master/uri/redis.txt). The example below demonstrates how the connection can easily be configured using a string, adhering to this specification. +go-redis also supports connecting via the +[redis uri specification](https://github.com/redis/redis-specifications/tree/master/uri/redis.txt). +The example below demonstrates how the connection can easily be configured using a string, adhering +to this specification. ```go import ( @@ -131,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 @@ -208,7 +227,8 @@ Lastly, run: go test ``` -Another option is to run your specific tests with an already running redis. The example below, tests against a redis running on port 9999.: +Another option is to run your specific tests with an already running redis. The example below, tests +against a redis running on port 9999.: ```shell REDIS_PORT=9999 go test diff --git a/backend/vendor/github.com/redis/go-redis/v9/bitmap_commands.go b/backend/vendor/github.com/redis/go-redis/v9/bitmap_commands.go index 1436b02..550d7f5 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/bitmap_commands.go +++ b/backend/vendor/github.com/redis/go-redis/v9/bitmap_commands.go @@ -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, "", "", "","") +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 +} diff --git a/backend/vendor/github.com/redis/go-redis/v9/command.go b/backend/vendor/github.com/redis/go-redis/v9/command.go index 00e356b..06ed86e 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/command.go +++ b/backend/vendor/github.com/redis/go-redis/v9/command.go @@ -1,11 +1,14 @@ package redis import ( + "bufio" "context" "fmt" "net" + "regexp" "strconv" "strings" + "sync" "time" "github.com/redis/go-redis/v9/internal" @@ -15,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) @@ -60,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) } @@ -80,10 +95,6 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { return 2 } } - - if info != nil { - return int(info.FirstKeyPos) - } return 1 } @@ -843,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) { @@ -947,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 { @@ -1042,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 { @@ -2704,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 { @@ -2842,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 { @@ -3302,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 { @@ -3383,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 { @@ -3573,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 { @@ -3672,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 { @@ -3736,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 { @@ -3800,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 { @@ -4650,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 { @@ -4752,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 { @@ -5225,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 { @@ -5298,3 +5309,165 @@ type LibraryInfo struct { LibName *string LibVer *string } + +// ------------------------------------------- + +type InfoCmd struct { + baseCmd + val map[string]map[string]string +} + +var _ Cmder = (*InfoCmd)(nil) + +func NewInfoCmd(ctx context.Context, args ...interface{}) *InfoCmd { + return &InfoCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *InfoCmd) SetVal(val map[string]map[string]string) { + cmd.val = val +} + +func (cmd *InfoCmd) Val() map[string]map[string]string { + return cmd.val +} + +func (cmd *InfoCmd) Result() (map[string]map[string]string, error) { + return cmd.val, cmd.err +} + +func (cmd *InfoCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *InfoCmd) readReply(rd *proto.Reader) error { + val, err := rd.ReadString() + if err != nil { + return err + } + + section := "" + scanner := bufio.NewScanner(strings.NewReader(val)) + moduleRe := regexp.MustCompile(`module:name=(.+?),(.+)$`) + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") { + if cmd.val == nil { + cmd.val = make(map[string]map[string]string) + } + section = strings.TrimPrefix(line, "# ") + cmd.val[section] = make(map[string]string) + } else if line != "" { + if section == "Modules" { + kv := moduleRe.FindStringSubmatch(line) + if len(kv) == 3 { + cmd.val[section][kv[1]] = kv[2] + } + } else { + kv := strings.SplitN(line, ":", 2) + if len(kv) == 2 { + cmd.val[section][kv[0]] = kv[1] + } + } + } + } + + return nil +} + +func (cmd *InfoCmd) Item(section, key string) string { + if cmd.val == nil { + return "" + } else if cmd.val[section] == nil { + return "" + } else { + 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 +} diff --git a/backend/vendor/github.com/redis/go-redis/v9/commands.go b/backend/vendor/github.com/redis/go-redis/v9/commands.go index d791753..546ebaf 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/commands.go +++ b/backend/vendor/github.com/redis/go-redis/v9/commands.go @@ -204,27 +204,28 @@ 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 ACLCmdable + BitMapCmdable + ClusterCmdable + GearsCmdable + GenericCmdable + GeoCmdable HashCmdable HyperLogLogCmdable - GeoCmdable - GenericCmdable ListCmdable + ProbabilisticCmdable + PubSubCmdable + ScriptingFunctionsCmdable SetCmdable SortedSetCmdable - ClusterCmdable - ScriptingFunctionsCmdable StringCmdable - PubSubCmdable - GearsCmdable - ProbabilisticCmdable - TimeseriesCmdable StreamCmdable + TimeseriesCmdable + JSONCmdable } type StatefulCmdable interface { @@ -569,6 +570,17 @@ func (c cmdable) Info(ctx context.Context, sections ...string) *StringCmd { return cmd } +func (c cmdable) InfoMap(ctx context.Context, sections ...string) *InfoCmd { + args := make([]interface{}, 1+len(sections)) + args[0] = "info" + for i, section := range sections { + args[i+1] = section + } + cmd := NewInfoCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + func (c cmdable) LastSave(ctx context.Context) *IntCmd { cmd := NewIntCmd(ctx, "lastsave") _ = c(ctx, cmd) @@ -687,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 +} diff --git a/backend/vendor/github.com/redis/go-redis/v9/error.go b/backend/vendor/github.com/redis/go-redis/v9/error.go index d5ebad6..8a59913 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/error.go +++ b/backend/vendor/github.com/redis/go-redis/v9/error.go @@ -2,6 +2,7 @@ package redis import ( "context" + "errors" "io" "net" "strings" @@ -15,11 +16,11 @@ var ErrClosed = pool.ErrClosed // HasErrorPrefix checks if the err is a Redis error and the message contains a prefix. func HasErrorPrefix(err error, prefix string) bool { - err, ok := err.(Error) - if !ok { + var rErr Error + if !errors.As(err, &rErr) { return false } - msg := err.Error() + msg := rErr.Error() msg = strings.TrimPrefix(msg, "ERR ") // KVRocks adds such prefix return strings.HasPrefix(msg, prefix) } diff --git a/backend/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go b/backend/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go index 234079f..1a560e4 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go +++ b/backend/vendor/github.com/redis/go-redis/v9/internal/hscan/structmap.go @@ -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 diff --git a/backend/vendor/github.com/redis/go-redis/v9/internal/pool/pool.go b/backend/vendor/github.com/redis/go-redis/v9/internal/pool/pool.go index f391ced..986c05d 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/internal/pool/pool.go +++ b/backend/vendor/github.com/redis/go-redis/v9/internal/pool/pool.go @@ -263,6 +263,7 @@ func (p *ConnPool) Get(ctx context.Context) (*Conn, error) { p.connsMu.Unlock() if err != nil { + p.freeTurn() return nil, err } diff --git a/backend/vendor/github.com/redis/go-redis/v9/internal/proto/writer.go b/backend/vendor/github.com/redis/go-redis/v9/internal/proto/writer.go index 8eaada1..78595cc 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/internal/proto/writer.go +++ b/backend/vendor/github.com/redis/go-redis/v9/internal/proto/writer.go @@ -65,37 +65,68 @@ func (w *Writer) WriteArg(v interface{}) error { return w.string("") case string: return w.string(v) + case *string: + return w.string(*v) case []byte: return w.bytes(v) case int: return w.int(int64(v)) + case *int: + return w.int(int64(*v)) case int8: return w.int(int64(v)) + case *int8: + return w.int(int64(*v)) case int16: return w.int(int64(v)) + case *int16: + return w.int(int64(*v)) case int32: return w.int(int64(v)) + case *int32: + return w.int(int64(*v)) case int64: return w.int(v) + case *int64: + return w.int(*v) case uint: return w.uint(uint64(v)) + case *uint: + return w.uint(uint64(*v)) case uint8: return w.uint(uint64(v)) + case *uint8: + return w.uint(uint64(*v)) case uint16: return w.uint(uint64(v)) + case *uint16: + return w.uint(uint64(*v)) case uint32: return w.uint(uint64(v)) + case *uint32: + return w.uint(uint64(*v)) case uint64: return w.uint(v) + case *uint64: + return w.uint(*v) case float32: return w.float(float64(v)) + case *float32: + return w.float(float64(*v)) case float64: return w.float(v) + case *float64: + return w.float(*v) case bool: if v { return w.int(1) } return w.int(0) + case *bool: + if *v { + return w.int(1) + } + return w.int(0) case time.Time: w.numBuf = v.AppendFormat(w.numBuf[:0], time.RFC3339Nano) return w.bytes(w.numBuf) diff --git a/backend/vendor/github.com/redis/go-redis/v9/internal/util/type.go b/backend/vendor/github.com/redis/go-redis/v9/internal/util/type.go new file mode 100644 index 0000000..a7ea712 --- /dev/null +++ b/backend/vendor/github.com/redis/go-redis/v9/internal/util/type.go @@ -0,0 +1,5 @@ +package util + +func ToPtr[T any](v T) *T { + return &v +} diff --git a/backend/vendor/github.com/redis/go-redis/v9/json.go b/backend/vendor/github.com/redis/go-redis/v9/json.go new file mode 100644 index 0000000..ca731db --- /dev/null +++ b/backend/vendor/github.com/redis/go-redis/v9/json.go @@ -0,0 +1,599 @@ +package redis + +import ( + "context" + "encoding/json" + "strings" + + "github.com/redis/go-redis/v9/internal/proto" + "github.com/redis/go-redis/v9/internal/util" +) + +// ------------------------------------------- + +type JSONCmdable interface { + JSONArrAppend(ctx context.Context, key, path string, values ...interface{}) *IntSliceCmd + JSONArrIndex(ctx context.Context, key, path string, value ...interface{}) *IntSliceCmd + JSONArrIndexWithArgs(ctx context.Context, key, path string, options *JSONArrIndexArgs, value ...interface{}) *IntSliceCmd + JSONArrInsert(ctx context.Context, key, path string, index int64, values ...interface{}) *IntSliceCmd + JSONArrLen(ctx context.Context, key, path string) *IntSliceCmd + JSONArrPop(ctx context.Context, key, path string, index int) *StringSliceCmd + JSONArrTrim(ctx context.Context, key, path string) *IntSliceCmd + JSONArrTrimWithArgs(ctx context.Context, key, path string, options *JSONArrTrimArgs) *IntSliceCmd + JSONClear(ctx context.Context, key, path string) *IntCmd + JSONDebugMemory(ctx context.Context, key, path string) *IntCmd + JSONDel(ctx context.Context, key, path string) *IntCmd + JSONForget(ctx context.Context, key, path string) *IntCmd + JSONGet(ctx context.Context, key string, paths ...string) *JSONCmd + JSONGetWithArgs(ctx context.Context, key string, options *JSONGetArgs, paths ...string) *JSONCmd + JSONMerge(ctx context.Context, key, path string, value string) *StatusCmd + JSONMSetArgs(ctx context.Context, docs []JSONSetArgs) *StatusCmd + JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd + JSONMGet(ctx context.Context, path string, keys ...string) *JSONSliceCmd + JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd + JSONObjKeys(ctx context.Context, key, path string) *SliceCmd + JSONObjLen(ctx context.Context, key, path string) *IntPointerSliceCmd + JSONSet(ctx context.Context, key, path string, value interface{}) *StatusCmd + JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd + JSONStrAppend(ctx context.Context, key, path, value string) *IntPointerSliceCmd + JSONStrLen(ctx context.Context, key, path string) *IntPointerSliceCmd + JSONToggle(ctx context.Context, key, path string) *IntPointerSliceCmd + JSONType(ctx context.Context, key, path string) *JSONSliceCmd +} + +type JSONSetArgs struct { + Key string + Path string + Value interface{} +} + +type JSONArrIndexArgs struct { + Start int + Stop *int +} + +type JSONArrTrimArgs struct { + Start int + Stop *int +} + +type JSONCmd struct { + baseCmd + val string + expanded []interface{} +} + +var _ Cmder = (*JSONCmd)(nil) + +func newJSONCmd(ctx context.Context, args ...interface{}) *JSONCmd { + return &JSONCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *JSONCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *JSONCmd) SetVal(val string) { + cmd.val = val +} + +func (cmd *JSONCmd) Val() string { + if len(cmd.val) == 0 && cmd.expanded != nil { + val, err := json.Marshal(cmd.expanded) + if err != nil { + cmd.SetErr(err) + return "" + } + return string(val) + + } else { + return cmd.val + } +} + +func (cmd *JSONCmd) Result() (string, error) { + return cmd.Val(), cmd.Err() +} + +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 { + return "", err + } + } + + return cmd.expanded, nil +} + +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 = "" + return Nil + } + + if readType, err := rd.PeekReplyType(); err != nil { + return err + } else if readType == proto.RespArray { + + size, err := rd.ReadArrayLen() + if err != nil { + return err + } + + expanded := make([]interface{}, size) + + for i := 0; i < size; i++ { + if expanded[i], err = rd.ReadReply(); err != nil { + return err + } + } + cmd.expanded = expanded + + } else { + if str, err := rd.ReadString(); err != nil && err != Nil { + return err + } else if str == "" || err == Nil { + cmd.val = "" + } else { + cmd.val = str + } + } + + return nil +} + +// ------------------------------------------- + +type JSONSliceCmd struct { + baseCmd + val []interface{} +} + +func NewJSONSliceCmd(ctx context.Context, args ...interface{}) *JSONSliceCmd { + return &JSONSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *JSONSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *JSONSliceCmd) SetVal(val []interface{}) { + cmd.val = val +} + +func (cmd *JSONSliceCmd) Val() []interface{} { + return cmd.val +} + +func (cmd *JSONSliceCmd) Result() ([]interface{}, error) { + return cmd.val, cmd.err +} + +func (cmd *JSONSliceCmd) readReply(rd *proto.Reader) error { + if cmd.baseCmd.Err() == Nil { + cmd.val = nil + return Nil + } + + if readType, err := rd.PeekReplyType(); err != nil { + return err + } else if readType == proto.RespArray { + response, err := rd.ReadReply() + if err != nil { + return nil + } else { + cmd.val = response.([]interface{}) + } + + } else { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + cmd.val = make([]interface{}, n) + for i := 0; i < len(cmd.val); i++ { + switch s, err := rd.ReadString(); { + case err == Nil: + cmd.val[i] = "" + case err != nil: + return err + default: + cmd.val[i] = s + } + } + } + return nil +} + +/******************************************************************************* +* +* IntPointerSliceCmd +* used to represent a RedisJSON response where the result is either an integer or nil +* +*******************************************************************************/ + +type IntPointerSliceCmd struct { + baseCmd + val []*int64 +} + +// NewIntPointerSliceCmd initialises an IntPointerSliceCmd +func NewIntPointerSliceCmd(ctx context.Context, args ...interface{}) *IntPointerSliceCmd { + return &IntPointerSliceCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *IntPointerSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *IntPointerSliceCmd) SetVal(val []*int64) { + cmd.val = val +} + +func (cmd *IntPointerSliceCmd) Val() []*int64 { + return cmd.val +} + +func (cmd *IntPointerSliceCmd) Result() ([]*int64, error) { + return cmd.val, cmd.err +} + +func (cmd *IntPointerSliceCmd) readReply(rd *proto.Reader) error { + n, err := rd.ReadArrayLen() + if err != nil { + return err + } + cmd.val = make([]*int64, n) + + for i := 0; i < len(cmd.val); i++ { + val, err := rd.ReadInt() + if err != nil && err != Nil { + return err + } else if err != Nil { + cmd.val[i] = &val + } + } + + return nil +} + +//------------------------------------------------------------------------------ + +// JSONArrAppend adds the provided JSON values to the end of the array at the given path. +// For more information, see https://redis.io/commands/json.arrappend +func (c cmdable) JSONArrAppend(ctx context.Context, key, path string, values ...interface{}) *IntSliceCmd { + args := []interface{}{"JSON.ARRAPPEND", key, path} + args = append(args, values...) + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrIndex searches for the first occurrence of the provided JSON value in the array at the given path. +// For more information, see https://redis.io/commands/json.arrindex +func (c cmdable) JSONArrIndex(ctx context.Context, key, path string, value ...interface{}) *IntSliceCmd { + args := []interface{}{"JSON.ARRINDEX", key, path} + args = append(args, value...) + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrIndexWithArgs searches for the first occurrence of a JSON value in an array while allowing the start and +// stop options to be provided. +// For more information, see https://redis.io/commands/json.arrindex +func (c cmdable) JSONArrIndexWithArgs(ctx context.Context, key, path string, options *JSONArrIndexArgs, value ...interface{}) *IntSliceCmd { + args := []interface{}{"JSON.ARRINDEX", key, path} + args = append(args, value...) + + if options != nil { + args = append(args, options.Start) + if options.Stop != nil { + args = append(args, *options.Stop) + } + } + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrInsert inserts the JSON values into the array at the specified path before the index (shifts to the right). +// For more information, see https://redis.io/commands/json.arrinsert +func (c cmdable) JSONArrInsert(ctx context.Context, key, path string, index int64, values ...interface{}) *IntSliceCmd { + args := []interface{}{"JSON.ARRINSERT", key, path, index} + args = append(args, values...) + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrLen reports the length of the JSON array at the specified path in the given key. +// For more information, see https://redis.io/commands/json.arrlen +func (c cmdable) JSONArrLen(ctx context.Context, key, path string) *IntSliceCmd { + args := []interface{}{"JSON.ARRLEN", key, path} + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrPop removes and returns an element from the specified index in the array. +// For more information, see https://redis.io/commands/json.arrpop +func (c cmdable) JSONArrPop(ctx context.Context, key, path string, index int) *StringSliceCmd { + args := []interface{}{"JSON.ARRPOP", key, path, index} + cmd := NewStringSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrTrim trims an array to contain only the specified inclusive range of elements. +// For more information, see https://redis.io/commands/json.arrtrim +func (c cmdable) JSONArrTrim(ctx context.Context, key, path string) *IntSliceCmd { + args := []interface{}{"JSON.ARRTRIM", key, path} + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONArrTrimWithArgs trims an array to contain only the specified inclusive range of elements. +// For more information, see https://redis.io/commands/json.arrtrim +func (c cmdable) JSONArrTrimWithArgs(ctx context.Context, key, path string, options *JSONArrTrimArgs) *IntSliceCmd { + args := []interface{}{"JSON.ARRTRIM", key, path} + + if options != nil { + args = append(args, options.Start) + + if options.Stop != nil { + args = append(args, *options.Stop) + } + } + cmd := NewIntSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONClear clears container values (arrays/objects) and sets numeric values to 0. +// For more information, see https://redis.io/commands/json.clear +func (c cmdable) JSONClear(ctx context.Context, key, path string) *IntCmd { + args := []interface{}{"JSON.CLEAR", key, path} + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONDebugMemory reports a value's memory usage in bytes (unimplemented) +// For more information, see https://redis.io/commands/json.debug-memory +func (c cmdable) JSONDebugMemory(ctx context.Context, key, path string) *IntCmd { + panic("not implemented") +} + +// JSONDel deletes a value. +// For more information, see https://redis.io/commands/json.del +func (c cmdable) JSONDel(ctx context.Context, key, path string) *IntCmd { + args := []interface{}{"JSON.DEL", key, path} + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONForget deletes a value. +// For more information, see https://redis.io/commands/json.forget +func (c cmdable) JSONForget(ctx context.Context, key, path string) *IntCmd { + args := []interface{}{"JSON.FORGET", key, path} + cmd := NewIntCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONGet returns the value at path in JSON serialized form. JSON.GET returns an +// array of strings. This function parses out the wrapping array but leaves the +// internal strings unprocessed by default (see Val()) +// For more information - https://redis.io/commands/json.get/ +func (c cmdable) JSONGet(ctx context.Context, key string, paths ...string) *JSONCmd { + args := make([]interface{}, len(paths)+2) + args[0] = "JSON.GET" + args[1] = key + for n, path := range paths { + args[n+2] = path + } + cmd := newJSONCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +type JSONGetArgs struct { + Indent string + Newline string + Space string +} + +// JSONGetWithArgs - Retrieves the value of a key from a JSON document. +// This function also allows for specifying additional options such as: +// Indention, NewLine and Space +// For more information - https://redis.io/commands/json.get/ +func (c cmdable) JSONGetWithArgs(ctx context.Context, key string, options *JSONGetArgs, paths ...string) *JSONCmd { + args := []interface{}{"JSON.GET", key} + if options != nil { + if options.Indent != "" { + args = append(args, "INDENT", options.Indent) + } + if options.Newline != "" { + args = append(args, "NEWLINE", options.Newline) + } + if options.Space != "" { + args = append(args, "SPACE", options.Space) + } + for _, path := range paths { + args = append(args, path) + } + } + cmd := newJSONCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONMerge merges a given JSON value into matching paths. +// For more information, see https://redis.io/commands/json.merge +func (c cmdable) JSONMerge(ctx context.Context, key, path string, value string) *StatusCmd { + args := []interface{}{"JSON.MERGE", key, path, value} + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONMGet returns the values at the specified path from multiple key arguments. +// Note - the arguments are reversed when compared with `JSON.MGET` as we want +// to follow the pattern of having the last argument be variable. +// For more information, see https://redis.io/commands/json.mget +func (c cmdable) JSONMGet(ctx context.Context, path string, keys ...string) *JSONSliceCmd { + args := make([]interface{}, len(keys)+1) + args[0] = "JSON.MGET" + for n, key := range keys { + args[n+1] = key + } + args = append(args, path) + cmd := NewJSONSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONMSetArgs sets or updates one or more JSON values according to the specified key-path-value triplets. +// For more information, see https://redis.io/commands/json.mset +func (c cmdable) JSONMSetArgs(ctx context.Context, docs []JSONSetArgs) *StatusCmd { + args := []interface{}{"JSON.MSET"} + for _, doc := range docs { + args = append(args, doc.Key, doc.Path, doc.Value) + } + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +func (c cmdable) JSONMSet(ctx context.Context, params ...interface{}) *StatusCmd { + args := []interface{}{"JSON.MSET"} + args = append(args, params...) + cmd := NewStatusCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONNumIncrBy increments the number value stored at the specified path by the provided number. +// For more information, see https://redis.io/commands/json.numincreby +func (c cmdable) JSONNumIncrBy(ctx context.Context, key, path string, value float64) *JSONCmd { + args := []interface{}{"JSON.NUMINCRBY", key, path, value} + cmd := newJSONCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONObjKeys returns the keys in the object that's referenced by the specified path. +// For more information, see https://redis.io/commands/json.objkeys +func (c cmdable) JSONObjKeys(ctx context.Context, key, path string) *SliceCmd { + args := []interface{}{"JSON.OBJKEYS", key, path} + cmd := NewSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONObjLen reports the number of keys in the JSON object at the specified path in the given key. +// For more information, see https://redis.io/commands/json.objlen +func (c cmdable) JSONObjLen(ctx context.Context, key, path string) *IntPointerSliceCmd { + args := []interface{}{"JSON.OBJLEN", key, path} + cmd := NewIntPointerSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONSet sets the JSON value at the given path in the given key. The value must be something that +// can be marshaled to JSON (using encoding/JSON) unless the argument is a string or a []byte when we assume that +// it can be passed directly as JSON. +// For more information, see https://redis.io/commands/json.set +func (c cmdable) JSONSet(ctx context.Context, key, path string, value interface{}) *StatusCmd { + return c.JSONSetMode(ctx, key, path, value, "") +} + +// JSONSetMode sets the JSON value at the given path in the given key and allows the mode to be set +// (the mode value must be "XX" or "NX"). The value must be something that can be marshaled to JSON (using encoding/JSON) unless +// the argument is a string or []byte when we assume that it can be passed directly as JSON. +// For more information, see https://redis.io/commands/json.set +func (c cmdable) JSONSetMode(ctx context.Context, key, path string, value interface{}, mode string) *StatusCmd { + var bytes []byte + var err error + switch v := value.(type) { + case string: + bytes = []byte(v) + case []byte: + bytes = v + default: + bytes, err = json.Marshal(v) + } + args := []interface{}{"JSON.SET", key, path, util.BytesToString(bytes)} + if mode != "" { + switch strings.ToUpper(mode) { + case "XX", "NX": + args = append(args, strings.ToUpper(mode)) + + default: + panic("redis: JSON.SET mode must be NX or XX") + } + } + cmd := NewStatusCmd(ctx, args...) + if err != nil { + cmd.SetErr(err) + } else { + _ = c(ctx, cmd) + } + return cmd +} + +// JSONStrAppend appends the JSON-string values to the string at the specified path. +// For more information, see https://redis.io/commands/json.strappend +func (c cmdable) JSONStrAppend(ctx context.Context, key, path, value string) *IntPointerSliceCmd { + args := []interface{}{"JSON.STRAPPEND", key, path, value} + cmd := NewIntPointerSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONStrLen reports the length of the JSON String at the specified path in the given key. +// For more information, see https://redis.io/commands/json.strlen +func (c cmdable) JSONStrLen(ctx context.Context, key, path string) *IntPointerSliceCmd { + args := []interface{}{"JSON.STRLEN", key, path} + cmd := NewIntPointerSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONToggle toggles a Boolean value stored at the specified path. +// For more information, see https://redis.io/commands/json.toggle +func (c cmdable) JSONToggle(ctx context.Context, key, path string) *IntPointerSliceCmd { + args := []interface{}{"JSON.TOGGLE", key, path} + cmd := NewIntPointerSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} + +// JSONType reports the type of JSON value at the specified path. +// For more information, see https://redis.io/commands/json.type +func (c cmdable) JSONType(ctx context.Context, key, path string) *JSONSliceCmd { + args := []interface{}{"JSON.TYPE", key, path} + cmd := NewJSONSliceCmd(ctx, args...) + _ = c(ctx, cmd) + return cmd +} diff --git a/backend/vendor/github.com/redis/go-redis/v9/options.go b/backend/vendor/github.com/redis/go-redis/v9/options.go index ba65def..dff52ae 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/options.go +++ b/backend/vendor/github.com/redis/go-redis/v9/options.go @@ -142,8 +142,11 @@ type Options struct { // Enables read only queries on slave/follower nodes. readOnly bool - // // Disable set-lib on connect. Default is false. + // Disable set-lib on connect. Default is false. DisableIndentity bool + + // Add suffix to client name. Default is empty. + IdentitySuffix string } func (opt *Options) init() { @@ -461,6 +464,7 @@ func setupConnParams(u *url.URL, o *Options) (*Options, error) { o.PoolTimeout = q.duration("pool_timeout") o.MinIdleConns = q.int("min_idle_conns") o.MaxIdleConns = q.int("max_idle_conns") + o.MaxActiveConns = q.int("max_active_conns") if q.has("conn_max_idle_time") { o.ConnMaxIdleTime = q.duration("conn_max_idle_time") } else { diff --git a/backend/vendor/github.com/redis/go-redis/v9/osscluster.go b/backend/vendor/github.com/redis/go-redis/v9/osscluster.go index b30e226..9e5eb04 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/osscluster.go +++ b/backend/vendor/github.com/redis/go-redis/v9/osscluster.go @@ -80,11 +80,14 @@ type ClusterOptions struct { PoolTimeout time.Duration MinIdleConns int MaxIdleConns int + MaxActiveConns int // applies per cluster node and not for the whole cluster ConnMaxIdleTime time.Duration ConnMaxLifetime time.Duration 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() { @@ -233,6 +236,8 @@ func setupClusterQueryParams(u *url.URL, o *ClusterOptions) (*ClusterOptions, er o.PoolFIFO = q.bool("pool_fifo") o.PoolSize = q.int("pool_size") o.MinIdleConns = q.int("min_idle_conns") + o.MaxIdleConns = q.int("max_idle_conns") + o.MaxActiveConns = q.int("max_active_conns") o.PoolTimeout = q.duration("pool_timeout") o.ConnMaxLifetime = q.duration("conn_max_lifetime") o.ConnMaxIdleTime = q.duration("conn_max_idle_time") @@ -274,18 +279,21 @@ func (opt *ClusterOptions) clientOptions() *Options { MinRetryBackoff: opt.MinRetryBackoff, MaxRetryBackoff: opt.MaxRetryBackoff, - DialTimeout: opt.DialTimeout, - ReadTimeout: opt.ReadTimeout, - WriteTimeout: opt.WriteTimeout, + DialTimeout: opt.DialTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + ContextTimeoutEnabled: opt.ContextTimeoutEnabled, PoolFIFO: opt.PoolFIFO, PoolSize: opt.PoolSize, PoolTimeout: opt.PoolTimeout, MinIdleConns: opt.MinIdleConns, MaxIdleConns: opt.MaxIdleConns, + MaxActiveConns: opt.MaxActiveConns, 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 @@ -902,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 @@ -916,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 } @@ -1778,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 { @@ -1792,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) @@ -1800,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) } diff --git a/backend/vendor/github.com/redis/go-redis/v9/package.json b/backend/vendor/github.com/redis/go-redis/v9/package.json index ffc2ab9..1a69004 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/package.json +++ b/backend/vendor/github.com/redis/go-redis/v9/package.json @@ -1,6 +1,6 @@ { "name": "redis", - "version": "9.2.1", + "version": "9.4.0", "main": "index.js", "repository": "git@github.com:redis/go-redis.git", "author": "Vladimir Mihailenco ", diff --git a/backend/vendor/github.com/redis/go-redis/v9/probabilistic.go b/backend/vendor/github.com/redis/go-redis/v9/probabilistic.go index d397b49..5d5cd1a 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/probabilistic.go +++ b/backend/vendor/github.com/redis/go-redis/v9/probabilistic.go @@ -150,12 +150,7 @@ func (c cmdable) BFReserveNonScaling(ctx context.Context, key string, errorRate func (c cmdable) BFReserveWithArgs(ctx context.Context, key string, options *BFReserveOptions) *StatusCmd { args := []interface{}{"BF.RESERVE", key} if options != nil { - if options.Error != 0 { - args = append(args, options.Error) - } - if options.Capacity != 0 { - args = append(args, options.Capacity) - } + args = append(args, options.Error, options.Capacity) if options.Expansion != 0 { args = append(args, "EXPANSION", options.Expansion) } diff --git a/backend/vendor/github.com/redis/go-redis/v9/pubsub.go b/backend/vendor/github.com/redis/go-redis/v9/pubsub.go index 16c0f56..5df537c 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/pubsub.go +++ b/backend/vendor/github.com/redis/go-redis/v9/pubsub.go @@ -487,11 +487,11 @@ func (c *PubSub) getContext() context.Context { // Channel returns a Go channel for concurrently receiving messages. // The channel is closed together with the PubSub. If the Go channel -// is blocked full for 30 seconds the message is dropped. +// is blocked full for 1 minute the message is dropped. // Receive* APIs can not be used after channel is created. // // go-redis periodically sends ping messages to test connection health -// and re-subscribes if ping can not not received for 30 seconds. +// and re-subscribes if ping can not not received for 1 minute. func (c *PubSub) Channel(opts ...ChannelOption) <-chan *Message { c.chOnce.Do(func() { c.msgCh = newChannel(c, opts...) diff --git a/backend/vendor/github.com/redis/go-redis/v9/redis.go b/backend/vendor/github.com/redis/go-redis/v9/redis.go index 9430eb7..4dd862b 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/redis.go +++ b/backend/vendor/github.com/redis/go-redis/v9/redis.go @@ -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} diff --git a/backend/vendor/github.com/redis/go-redis/v9/ring.go b/backend/vendor/github.com/redis/go-redis/v9/ring.go index 7cf0911..4ae0054 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/ring.go +++ b/backend/vendor/github.com/redis/go-redis/v9/ring.go @@ -79,9 +79,10 @@ type RingOptions struct { MinRetryBackoff time.Duration MaxRetryBackoff time.Duration - DialTimeout time.Duration - ReadTimeout time.Duration - WriteTimeout time.Duration + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + ContextTimeoutEnabled bool // PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO). PoolFIFO bool @@ -90,11 +91,15 @@ type RingOptions struct { PoolTimeout time.Duration MinIdleConns int MaxIdleConns int + MaxActiveConns int ConnMaxIdleTime time.Duration ConnMaxLifetime time.Duration TLSConfig *tls.Config Limiter Limiter + + DisableIndentity bool + IdentitySuffix string } func (opt *RingOptions) init() { @@ -144,20 +149,25 @@ func (opt *RingOptions) clientOptions() *Options { MaxRetries: -1, - DialTimeout: opt.DialTimeout, - ReadTimeout: opt.ReadTimeout, - WriteTimeout: opt.WriteTimeout, + DialTimeout: opt.DialTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + ContextTimeoutEnabled: opt.ContextTimeoutEnabled, PoolFIFO: opt.PoolFIFO, PoolSize: opt.PoolSize, PoolTimeout: opt.PoolTimeout, MinIdleConns: opt.MinIdleConns, MaxIdleConns: opt.MaxIdleConns, + MaxActiveConns: opt.MaxActiveConns, ConnMaxIdleTime: opt.ConnMaxIdleTime, ConnMaxLifetime: opt.ConnMaxLifetime, TLSConfig: opt.TLSConfig, Limiter: opt.Limiter, + + DisableIndentity: opt.DisableIndentity, + IdentitySuffix: opt.IdentitySuffix, } } @@ -670,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() } @@ -752,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) } diff --git a/backend/vendor/github.com/redis/go-redis/v9/sentinel.go b/backend/vendor/github.com/redis/go-redis/v9/sentinel.go index dbff406..9ace088 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/sentinel.go +++ b/backend/vendor/github.com/redis/go-redis/v9/sentinel.go @@ -74,10 +74,14 @@ type FailoverOptions struct { PoolTimeout time.Duration MinIdleConns int MaxIdleConns int + MaxActiveConns int ConnMaxIdleTime time.Duration ConnMaxLifetime time.Duration TLSConfig *tls.Config + + DisableIndentity bool + IdentitySuffix string } func (opt *FailoverOptions) clientOptions() *Options { @@ -107,10 +111,14 @@ func (opt *FailoverOptions) clientOptions() *Options { PoolTimeout: opt.PoolTimeout, MinIdleConns: opt.MinIdleConns, MaxIdleConns: opt.MaxIdleConns, + MaxActiveConns: opt.MaxActiveConns, ConnMaxIdleTime: opt.ConnMaxIdleTime, ConnMaxLifetime: opt.ConnMaxLifetime, TLSConfig: opt.TLSConfig, + + DisableIndentity: opt.DisableIndentity, + IdentitySuffix: opt.IdentitySuffix, } } @@ -130,15 +138,17 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options { MinRetryBackoff: opt.MinRetryBackoff, MaxRetryBackoff: opt.MaxRetryBackoff, - DialTimeout: opt.DialTimeout, - ReadTimeout: opt.ReadTimeout, - WriteTimeout: opt.WriteTimeout, + DialTimeout: opt.DialTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + ContextTimeoutEnabled: opt.ContextTimeoutEnabled, PoolFIFO: opt.PoolFIFO, PoolSize: opt.PoolSize, PoolTimeout: opt.PoolTimeout, MinIdleConns: opt.MinIdleConns, MaxIdleConns: opt.MaxIdleConns, + MaxActiveConns: opt.MaxActiveConns, ConnMaxIdleTime: opt.ConnMaxIdleTime, ConnMaxLifetime: opt.ConnMaxLifetime, @@ -165,15 +175,17 @@ func (opt *FailoverOptions) clusterOptions() *ClusterOptions { MinRetryBackoff: opt.MinRetryBackoff, MaxRetryBackoff: opt.MaxRetryBackoff, - DialTimeout: opt.DialTimeout, - ReadTimeout: opt.ReadTimeout, - WriteTimeout: opt.WriteTimeout, + DialTimeout: opt.DialTimeout, + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + ContextTimeoutEnabled: opt.ContextTimeoutEnabled, PoolFIFO: opt.PoolFIFO, PoolSize: opt.PoolSize, PoolTimeout: opt.PoolTimeout, MinIdleConns: opt.MinIdleConns, MaxIdleConns: opt.MaxIdleConns, + MaxActiveConns: opt.MaxActiveConns, ConnMaxIdleTime: opt.ConnMaxIdleTime, ConnMaxLifetime: opt.ConnMaxLifetime, diff --git a/backend/vendor/github.com/redis/go-redis/v9/timeseries_commands.go b/backend/vendor/github.com/redis/go-redis/v9/timeseries_commands.go index 61cc3a5..6f1b2fa 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/timeseries_commands.go +++ b/backend/vendor/github.com/redis/go-redis/v9/timeseries_commands.go @@ -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"} diff --git a/backend/vendor/github.com/redis/go-redis/v9/universal.go b/backend/vendor/github.com/redis/go-redis/v9/universal.go index 53ece18..275bef3 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/universal.go +++ b/backend/vendor/github.com/redis/go-redis/v9/universal.go @@ -48,6 +48,7 @@ type UniversalOptions struct { PoolTimeout time.Duration MinIdleConns int MaxIdleConns int + MaxActiveConns int ConnMaxIdleTime time.Duration ConnMaxLifetime time.Duration @@ -64,6 +65,9 @@ type UniversalOptions struct { // Only failover clients. MasterName string + + DisableIndentity bool + IdentitySuffix string } // Cluster returns cluster options created from the universal options. @@ -102,10 +106,14 @@ func (o *UniversalOptions) Cluster() *ClusterOptions { PoolTimeout: o.PoolTimeout, MinIdleConns: o.MinIdleConns, MaxIdleConns: o.MaxIdleConns, + MaxActiveConns: o.MaxActiveConns, ConnMaxIdleTime: o.ConnMaxIdleTime, ConnMaxLifetime: o.ConnMaxLifetime, TLSConfig: o.TLSConfig, + + DisableIndentity: o.DisableIndentity, + IdentitySuffix: o.IdentitySuffix, } } @@ -144,10 +152,14 @@ func (o *UniversalOptions) Failover() *FailoverOptions { PoolTimeout: o.PoolTimeout, MinIdleConns: o.MinIdleConns, MaxIdleConns: o.MaxIdleConns, + MaxActiveConns: o.MaxActiveConns, ConnMaxIdleTime: o.ConnMaxIdleTime, ConnMaxLifetime: o.ConnMaxLifetime, TLSConfig: o.TLSConfig, + + DisableIndentity: o.DisableIndentity, + IdentitySuffix: o.IdentitySuffix, } } @@ -183,10 +195,14 @@ func (o *UniversalOptions) Simple() *Options { PoolTimeout: o.PoolTimeout, MinIdleConns: o.MinIdleConns, MaxIdleConns: o.MaxIdleConns, + MaxActiveConns: o.MaxActiveConns, ConnMaxIdleTime: o.ConnMaxIdleTime, ConnMaxLifetime: o.ConnMaxLifetime, TLSConfig: o.TLSConfig, + + DisableIndentity: o.DisableIndentity, + IdentitySuffix: o.IdentitySuffix, } } diff --git a/backend/vendor/github.com/redis/go-redis/v9/version.go b/backend/vendor/github.com/redis/go-redis/v9/version.go index ddcecfb..92f4982 100644 --- a/backend/vendor/github.com/redis/go-redis/v9/version.go +++ b/backend/vendor/github.com/redis/go-redis/v9/version.go @@ -2,5 +2,5 @@ package redis // Version is the current release version. func Version() string { - return "9.2.1" + return "9.4.0" } diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index fd4b014..b057213 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -65,7 +65,7 @@ github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/danger github.com/pelletier/go-toml/v2/internal/tracker github.com/pelletier/go-toml/v2/unstable -# github.com/redis/go-redis/v9 v9.2.1 +# 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