2012-07-25 17:00:50 +04:00
|
|
|
package redis
|
|
|
|
|
|
|
|
import (
|
2020-03-11 17:26:42 +03:00
|
|
|
"context"
|
2023-03-22 19:30:20 +03:00
|
|
|
"encoding"
|
2018-02-22 15:14:30 +03:00
|
|
|
"errors"
|
2023-09-20 10:33:09 +03:00
|
|
|
"fmt"
|
2014-11-13 15:26:14 +03:00
|
|
|
"io"
|
2023-03-22 19:30:20 +03:00
|
|
|
"net"
|
2021-12-17 05:54:43 +03:00
|
|
|
"reflect"
|
2023-09-20 10:33:09 +03:00
|
|
|
"runtime"
|
2023-01-19 10:31:31 +03:00
|
|
|
"strings"
|
2014-05-11 11:42:40 +04:00
|
|
|
"time"
|
2016-04-09 14:52:01 +03:00
|
|
|
|
2023-01-23 09:48:54 +03:00
|
|
|
"github.com/redis/go-redis/v9/internal"
|
2012-07-25 17:00:50 +04:00
|
|
|
)
|
|
|
|
|
2021-06-25 09:51:18 +03:00
|
|
|
// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0,
|
|
|
|
// otherwise you will receive an error: (error) ERR syntax error.
|
2021-03-27 12:49:55 +03:00
|
|
|
// For example:
|
|
|
|
//
|
2022-10-07 22:40:51 +03:00
|
|
|
// rdb.Set(ctx, key, value, redis.KeepTTL)
|
2021-03-27 12:49:55 +03:00
|
|
|
const KeepTTL = -1
|
2020-09-18 14:49:12 +03:00
|
|
|
|
2015-01-31 12:08:56 +03:00
|
|
|
func usePrecise(dur time.Duration) bool {
|
|
|
|
return dur < time.Second || dur%time.Second != 0
|
|
|
|
}
|
|
|
|
|
2020-07-18 09:04:36 +03:00
|
|
|
func formatMs(ctx context.Context, dur time.Duration) int64 {
|
2015-04-07 12:42:16 +03:00
|
|
|
if dur > 0 && dur < time.Millisecond {
|
2019-06-17 12:32:40 +03:00
|
|
|
internal.Logger.Printf(
|
2020-07-18 09:04:36 +03:00
|
|
|
ctx,
|
2020-06-10 17:22:06 +03:00
|
|
|
"specified duration is %s, but minimal supported value is %s - truncating to 1ms",
|
2015-04-07 12:42:16 +03:00
|
|
|
dur, time.Millisecond,
|
|
|
|
)
|
2020-06-10 14:52:12 +03:00
|
|
|
return 1
|
2015-04-07 12:42:16 +03:00
|
|
|
}
|
2017-03-24 13:48:32 +03:00
|
|
|
return int64(dur / time.Millisecond)
|
2015-01-31 12:08:56 +03:00
|
|
|
}
|
|
|
|
|
2020-07-18 09:04:36 +03:00
|
|
|
func formatSec(ctx context.Context, dur time.Duration) int64 {
|
2015-04-07 12:42:16 +03:00
|
|
|
if dur > 0 && dur < time.Second {
|
2019-06-17 12:32:40 +03:00
|
|
|
internal.Logger.Printf(
|
2020-07-18 09:04:36 +03:00
|
|
|
ctx,
|
2020-06-10 16:44:48 +03:00
|
|
|
"specified duration is %s, but minimal supported value is %s - truncating to 1s",
|
2015-04-07 12:42:16 +03:00
|
|
|
dur, time.Second,
|
|
|
|
)
|
2020-06-10 14:52:12 +03:00
|
|
|
return 1
|
2015-04-07 12:42:16 +03:00
|
|
|
}
|
2017-03-24 13:48:32 +03:00
|
|
|
return int64(dur / time.Second)
|
2015-05-28 15:51:19 +03:00
|
|
|
}
|
|
|
|
|
2018-03-08 15:30:27 +03:00
|
|
|
func appendArgs(dst, src []interface{}) []interface{} {
|
|
|
|
if len(src) == 1 {
|
2020-06-29 17:48:57 +03:00
|
|
|
return appendArg(dst, src[0])
|
2018-03-08 15:30:27 +03:00
|
|
|
}
|
|
|
|
|
2019-07-25 13:28:15 +03:00
|
|
|
dst = append(dst, src...)
|
2018-03-08 15:30:27 +03:00
|
|
|
return dst
|
|
|
|
}
|
|
|
|
|
2020-06-29 17:48:57 +03:00
|
|
|
func appendArg(dst []interface{}, arg interface{}) []interface{} {
|
|
|
|
switch arg := arg.(type) {
|
|
|
|
case []string:
|
|
|
|
for _, s := range arg {
|
|
|
|
dst = append(dst, s)
|
|
|
|
}
|
|
|
|
return dst
|
|
|
|
case []interface{}:
|
|
|
|
dst = append(dst, arg...)
|
|
|
|
return dst
|
|
|
|
case map[string]interface{}:
|
|
|
|
for k, v := range arg {
|
|
|
|
dst = append(dst, k, v)
|
|
|
|
}
|
|
|
|
return dst
|
2021-04-09 06:27:11 +03:00
|
|
|
case map[string]string:
|
|
|
|
for k, v := range arg {
|
|
|
|
dst = append(dst, k, v)
|
|
|
|
}
|
|
|
|
return dst
|
2023-03-22 19:30:20 +03:00
|
|
|
case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP:
|
|
|
|
return append(dst, arg)
|
2020-06-29 17:48:57 +03:00
|
|
|
default:
|
2023-01-19 10:31:31 +03:00
|
|
|
// scan struct field
|
|
|
|
v := reflect.ValueOf(arg)
|
|
|
|
if v.Type().Kind() == reflect.Ptr {
|
|
|
|
if v.IsNil() {
|
|
|
|
// error: arg is not a valid object
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
v = v.Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Type().Kind() == reflect.Struct {
|
|
|
|
return appendStructField(dst, v)
|
|
|
|
}
|
|
|
|
|
2020-06-29 17:48:57 +03:00
|
|
|
return append(dst, arg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 10:31:31 +03:00
|
|
|
// appendStructField appends the field and value held by the structure v to dst, and returns the appended dst.
|
|
|
|
func appendStructField(dst []interface{}, v reflect.Value) []interface{} {
|
|
|
|
typ := v.Type()
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
|
|
tag := typ.Field(i).Tag.Get("redis")
|
|
|
|
if tag == "" || tag == "-" {
|
|
|
|
continue
|
|
|
|
}
|
2023-04-03 10:43:18 +03:00
|
|
|
name, opt, _ := strings.Cut(tag, ",")
|
|
|
|
if name == "" {
|
2023-01-19 10:31:31 +03:00
|
|
|
continue
|
|
|
|
}
|
2021-12-17 05:54:43 +03:00
|
|
|
|
2023-01-19 10:31:31 +03:00
|
|
|
field := v.Field(i)
|
2023-04-03 10:43:18 +03:00
|
|
|
|
|
|
|
// miss field
|
|
|
|
if omitEmpty(opt) && isEmptyValue(field) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-01-19 10:31:31 +03:00
|
|
|
if field.CanInterface() {
|
2023-04-03 10:43:18 +03:00
|
|
|
dst = append(dst, name, field.Interface())
|
2021-12-17 05:54:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 10:31:31 +03:00
|
|
|
return dst
|
2021-12-17 05:54:43 +03:00
|
|
|
}
|
|
|
|
|
2023-04-03 10:43:18 +03:00
|
|
|
func omitEmpty(opt string) bool {
|
|
|
|
for opt != "" {
|
|
|
|
var name string
|
|
|
|
name, opt, _ = strings.Cut(opt, ",")
|
|
|
|
if name == "omitempty" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func isEmptyValue(v reflect.Value) bool {
|
|
|
|
switch v.Kind() {
|
|
|
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
|
|
return v.Len() == 0
|
|
|
|
case reflect.Bool:
|
|
|
|
return !v.Bool()
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
return v.Int() == 0
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
return v.Uint() == 0
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
return v.Float() == 0
|
|
|
|
case reflect.Interface, reflect.Pointer:
|
|
|
|
return v.IsNil()
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-07-21 16:24:58 +03:00
|
|
|
type Cmdable interface {
|
2017-05-02 18:00:53 +03:00
|
|
|
Pipeline() Pipeliner
|
2020-03-11 17:26:42 +03:00
|
|
|
Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
2016-09-27 12:24:14 +03:00
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
2017-09-25 11:48:44 +03:00
|
|
|
TxPipeline() Pipeliner
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
Command(ctx context.Context) *CommandsInfoCmd
|
2023-03-19 13:20:42 +03:00
|
|
|
CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd
|
2023-03-25 13:57:12 +03:00
|
|
|
CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd
|
|
|
|
CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
ClientGetName(ctx context.Context) *StringCmd
|
|
|
|
Echo(ctx context.Context, message interface{}) *StringCmd
|
|
|
|
Ping(ctx context.Context) *StatusCmd
|
|
|
|
Quit(ctx context.Context) *StatusCmd
|
|
|
|
Unlink(ctx context.Context, keys ...string) *IntCmd
|
|
|
|
|
|
|
|
BgRewriteAOF(ctx context.Context) *StatusCmd
|
|
|
|
BgSave(ctx context.Context) *StatusCmd
|
|
|
|
ClientKill(ctx context.Context, ipPort string) *StatusCmd
|
|
|
|
ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd
|
|
|
|
ClientList(ctx context.Context) *StringCmd
|
2023-04-24 16:18:33 +03:00
|
|
|
ClientInfo(ctx context.Context) *ClientInfoCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
ClientPause(ctx context.Context, dur time.Duration) *BoolCmd
|
2022-06-04 15:02:53 +03:00
|
|
|
ClientUnpause(ctx context.Context) *BoolCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
ClientID(ctx context.Context) *IntCmd
|
2021-08-02 14:01:01 +03:00
|
|
|
ClientUnblock(ctx context.Context, id int64) *IntCmd
|
|
|
|
ClientUnblockWithError(ctx context.Context, id int64) *IntCmd
|
2021-04-27 10:04:46 +03:00
|
|
|
ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
ConfigResetStat(ctx context.Context) *StatusCmd
|
|
|
|
ConfigSet(ctx context.Context, parameter, value string) *StatusCmd
|
|
|
|
ConfigRewrite(ctx context.Context) *StatusCmd
|
|
|
|
DBSize(ctx context.Context) *IntCmd
|
|
|
|
FlushAll(ctx context.Context) *StatusCmd
|
|
|
|
FlushAllAsync(ctx context.Context) *StatusCmd
|
|
|
|
FlushDB(ctx context.Context) *StatusCmd
|
|
|
|
FlushDBAsync(ctx context.Context) *StatusCmd
|
|
|
|
Info(ctx context.Context, section ...string) *StringCmd
|
|
|
|
LastSave(ctx context.Context) *IntCmd
|
|
|
|
Save(ctx context.Context) *StatusCmd
|
|
|
|
Shutdown(ctx context.Context) *StatusCmd
|
|
|
|
ShutdownSave(ctx context.Context) *StatusCmd
|
|
|
|
ShutdownNoSave(ctx context.Context) *StatusCmd
|
|
|
|
SlaveOf(ctx context.Context, host, port string) *StatusCmd
|
2021-08-02 14:01:01 +03:00
|
|
|
SlowLogGet(ctx context.Context, num int64) *SlowLogCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
Time(ctx context.Context) *TimeCmd
|
|
|
|
DebugObject(ctx context.Context, key string) *StringCmd
|
2023-09-20 16:08:24 +03:00
|
|
|
MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
|
2023-04-18 16:03:47 +03:00
|
|
|
|
|
|
|
ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
|
2023-08-15 10:19:19 +03:00
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
ACLCmdable
|
2023-10-30 15:32:13 +03:00
|
|
|
BitMapCmdable
|
|
|
|
ClusterCmdable
|
|
|
|
GearsCmdable
|
|
|
|
GenericCmdable
|
|
|
|
GeoCmdable
|
2023-09-20 16:08:24 +03:00
|
|
|
HashCmdable
|
|
|
|
HyperLogLogCmdable
|
|
|
|
ListCmdable
|
2023-10-30 15:32:13 +03:00
|
|
|
ProbabilisticCmdable
|
|
|
|
PubSubCmdable
|
|
|
|
ScriptingFunctionsCmdable
|
2023-09-20 16:08:24 +03:00
|
|
|
SetCmdable
|
|
|
|
SortedSetCmdable
|
|
|
|
StringCmdable
|
2023-10-30 15:09:25 +03:00
|
|
|
StreamCmdable
|
2023-09-11 14:51:31 +03:00
|
|
|
TimeseriesCmdable
|
2023-10-30 15:09:25 +03:00
|
|
|
JSONCmdable
|
2016-07-21 08:50:28 +03:00
|
|
|
}
|
|
|
|
|
2017-05-01 18:42:58 +03:00
|
|
|
type StatefulCmdable interface {
|
2017-05-25 13:38:04 +03:00
|
|
|
Cmdable
|
2020-03-11 17:26:42 +03:00
|
|
|
Auth(ctx context.Context, password string) *StatusCmd
|
2020-05-21 10:16:44 +03:00
|
|
|
AuthACL(ctx context.Context, username, password string) *StatusCmd
|
2020-03-11 17:26:42 +03:00
|
|
|
Select(ctx context.Context, index int) *StatusCmd
|
|
|
|
SwapDB(ctx context.Context, index1, index2 int) *StatusCmd
|
|
|
|
ClientSetName(ctx context.Context, name string) *BoolCmd
|
2023-09-14 16:24:23 +03:00
|
|
|
ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd
|
2024-01-15 23:45:49 +03:00
|
|
|
ClientTracking(ctx context.Context, on bool, options *ClientTrackingOptions) *StatusCmd
|
|
|
|
ClientTrackingOn(ctx context.Context) *StatusCmd
|
|
|
|
ClientTrackingOff(ctx context.Context) *StatusCmd
|
|
|
|
ClientTrackingOnWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd
|
|
|
|
ClientTrackingOffWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd
|
|
|
|
ClientTrackingInfo(ctx context.Context) *MapStringInterfaceCmd
|
2021-04-27 10:04:46 +03:00
|
|
|
Hello(ctx context.Context, ver int, username, password, clientName string) *MapStringInterfaceCmd
|
2017-05-01 18:42:58 +03:00
|
|
|
}
|
|
|
|
|
2020-07-16 09:52:07 +03:00
|
|
|
var (
|
|
|
|
_ Cmdable = (*Client)(nil)
|
|
|
|
_ Cmdable = (*Tx)(nil)
|
|
|
|
_ Cmdable = (*Ring)(nil)
|
|
|
|
_ Cmdable = (*ClusterClient)(nil)
|
|
|
|
)
|
2016-12-14 13:12:50 +03:00
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
type cmdable func(ctx context.Context, cmd Cmder) error
|
2017-05-25 13:38:04 +03:00
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
type statefulCmdable func(ctx context.Context, cmd Cmder) error
|
2017-05-25 13:38:04 +03:00
|
|
|
|
2012-07-27 18:21:50 +04:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c statefulCmdable) Auth(ctx context.Context, password string) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "auth", password)
|
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2021-06-23 13:47:10 +03:00
|
|
|
// AuthACL Perform an AUTH command, using the given user and pass.
|
2020-05-21 08:59:20 +03:00
|
|
|
// Should be used to authenticate the current connection with one of the connections defined in the ACL list
|
|
|
|
// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
2020-05-21 10:19:17 +03:00
|
|
|
func (c statefulCmdable) AuthACL(ctx context.Context, username, password string) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "auth", username, password)
|
|
|
|
_ = c(ctx, cmd)
|
2020-05-21 08:59:20 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c cmdable) Wait(ctx context.Context, numSlaves int, timeout time.Duration) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "wait", numSlaves, int(timeout/time.Millisecond))
|
chore: sync master (#2051)
* Upgrade redis-server version (#1833)
* Upgrade redis-server version
Signed-off-by: monkey <golang@88.com>
* XAutoClaim changed the return value
Signed-off-by: monkey <golang@88.com>
* add cmd: geosearch, geosearchstore (#1836)
* add cmd: geosearch, geosearchstore
Signed-off-by: monkey92t <golang@88.com>
* GeoSearchQuery and GeoSearchLocationQuery changed to pointer passing
Signed-off-by: monkey92t <golang@88.com>
* Added missing method XInfoStreamFull to Cmdable interface
* Run go mod tidy in redisotel
Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
* Revert "ConnPool check fd for bad conns (#1824)" (#1849)
This reverts commit 346bfafddd36dd52d51b064033048de5552ee91e.
* Automate release process (#1852)
* Bump github.com/onsi/gomega from 1.10.5 to 1.14.0 (#1832)
* Bump github.com/onsi/gomega from 1.10.5 to 1.14.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.5 to 1.14.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.10.5...v1.14.0)
---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* Upgrade gomega to v1.15.0
Signed-off-by: monkey92t <golang@88.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: monkey92t <golang@88.com>
* Add version.go
* Fix otel example
* Fix package name in release script
* More fixes for otel example
* And more
* Fix release.sh
* Release v8.11.3 (release.sh)
* Create an annotated tag to give release.yml chance to run
* Tweak tag.sh
* Add Cmd.Slice helper to cast to []interface{} (#1859)
* after the connection pool is closed, no new connections should be added (#1863)
* after the connection pool is closed, no new connections should be added
Signed-off-by: monkey92t <golang@88.com>
* remove runGoroutine
Signed-off-by: monkey92t <golang@88.com>
* pool.popIdle add p.closed check
Signed-off-by: monkey92t <golang@88.com>
* upgrade golangci-lint v1.42.0
Signed-off-by: monkey92t <golang@88.com>
* Bump github.com/onsi/gomega from 1.15.0 to 1.16.0 (#1865)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.16.0)
---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add go 1.17 to the build matrix
* Remove go 1.15 from build matrix
* Add scan struct example (#1870)
* Replace release job
* Bump github.com/cespare/xxhash/v2 from 2.1.1 to 2.1.2 (#1872)
Bumps [github.com/cespare/xxhash/v2](https://github.com/cespare/xxhash) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/cespare/xxhash/releases)
- [Commits](https://github.com/cespare/xxhash/compare/v2.1.1...v2.1.2)
---
updated-dependencies:
- dependency-name: github.com/cespare/xxhash/v2
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Fix tag script to push tag by tag
* Fix releasing.md
* Fix/pubsub ping mutex (#1878)
* Fix PubSub.Ping to hold the lock
* Fix PubSub.Ping to hold the lock
* add write cmd data-race test
Signed-off-by: monkey92t <golang@88.com>
Co-authored-by: monkey92t <golang@88.com>
* chore: cleanup OpenTelemetry example
* chore: gofmt all code
* Refactor TestParseURL
This is in preparation for supporting query parameters
in ParseURL:
- use an expected *Options instance to execute assertions on
- extract assertions into helper function
- enable parallel testing
- condense test table
* Add query parameter parsing to ParseURL()
Before this change, ParseURL would only accept a very restricted
set of URLs (it returned an error, if it encountered any parameter).
This commit introduces the ability to process URLs like
redis://localhost/1?dial_timeout=10s
and similar.
Go programs which were providing a configuration tunable (e.g.
CLI flag, config entry or environment variable) to configure
the Redis connection now don't need to perform this task
themselves.
* chore: add links to readme
* chore: fix discussions link
* empty hooks.withContext removed
* chore: gofmt
* chore: use conventional commits and auto-generate changelog
* feat: add acl auth support for sentinels
* chore: swap to acl auth at the test-level
* Add support for BLMove command
* chore: update dependencies
* chore: update link
* feat: add SetVal method for each command
* feat: add Cmd.{String,Int,Float,Bool}Slice helpers and an example
* chore: tweak GH actions to run all jobs
* chore: add Lua scripting example
* Fix Redis Cluster issue during roll outs of new nodes with same addr (#1914)
* fix: recycle connections in some Redis Cluster scenarios
This issue was surfaced in a Cloud Provider solution that used for
rolling out new nodes using the same address (hostname) of the nodes
that will be replaced in a Redis Cluster, while the former ones once
depromoted as Slaves would continue in service during some mintues
for redirecting traffic.
The solution basically identifies when the connection could be stale
since a MOVED response will be returned using the same address (hostname)
that is being used by the connection. At that moment we consider the
connection as no longer usable forcing to recycle the connection.
* chore: lazy reload when moved or ask
* chore: use conv commit message
* chore: release v8.11.4 (release.sh)
* fix: add whitespace for avoid unlikely colisions
* fix: format
* chore: fix links
* chore: use ctx parameter in cmdInfo
* Bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5 (#1925)
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.4 to 1.16.5.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.16.4...v1.16.5)
---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add support for time.Duration write and scan
* test: add test case for setting and scanning durations
* chore: fix linter
* fix(extra/redisotel): set span.kind attribute to client
According to the opentelemetry specification this should always be set to client for database client
libraries.
I've also removed the SetAttributes call and instead set the attributes during creation of the span.
This is what the library SHOULD be doing according to the opentelemetry api specification.
* chore: update otel example
* fix: update some argument counts in pre-allocs
In some cases number of pre-allocated places in
argument array is missing 1 or 2 elements,
which results in re-allocation of twice as large array
* chore: add example how to delete keys without a ttl
* chore: don't enable all lints
* chore(deps): bump github.com/onsi/gomega from 1.16.0 to 1.17.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.16.0...v1.17.0)
---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* feat: Add redis v7's NX, XX, GT, LT expire variants
* chore: add missing readme
* chore: tweak feature links
* chore: remove Discord
* fix: set timeout for WAIT command. Fixes #1963
* build: update `go` directive in `go.mod` to 1.17
This commit enables support for module graph pruning and lazy module
loading for projects that are at Go 1.17 or higher.
Reference: https://go.dev/ref/mod#go-mod-file-go
Reference: https://go.dev/ref/mod#graph-pruning
Reference: https://go.dev/ref/mod#lazy-loading
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
* chore: update link
* chore: export cmder.SetFirstKeyPos to support build module commands
* feat(redisotel): ability to override TracerProvider (#1998)
* fix: add missing Expire methods to Cmdable
This is a followup to https://github.com/go-redis/redis/pull/1928
* chore(deps): bump github.com/onsi/gomega from 1.17.0 to 1.18.1
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.17.0 to 1.18.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.17.0...v1.18.1)
---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* Update README.md (#2011)
chore: add fmt library in example code
* chore: instrumentation name and version (#2012)
* fix: invalid type assert in stringArg
* chore: cleanup
* fix: example/otel compile error (#2028)
* fix: rename Golang to Go (#2030)
https://go.dev/doc/faq#go_or_golang
* feat: add support for passing extra attributes added to spans
* feat: set net.peer.name and net.peer.port in otel example
* chore: tweak Uptrace copy
* feat: add support for COPY command (#2016)
* feat: add support for acl sentinel auth in universal client
* chore(deps): bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* chore: add hll example
* chore: tweak release script
* chore: release v8.11.5 (release.sh)
* chore: add discord back
Co-authored-by: Eugene Ponizovsky <ponizovsky@gmail.com>
Co-authored-by: Bogdan Drutu <bogdandrutu@gmail.com>
Co-authored-by: Vladimir Mihailenco <vladimir.webdev@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kishan B <kishancs46@gmail.com>
Co-authored-by: Dominik Menke <dom@digineo.de>
Co-authored-by: Gökhan Özeloğlu <gozeloglu@gmail.com>
Co-authored-by: Justin Sievenpiper <justin@sievenpiper.co>
Co-authored-by: Алексей Романовский <aromanovsky@epiphan.com>
Co-authored-by: Stavros Panakakakis <stavrospanakakis@gmail.com>
Co-authored-by: Pau Freixes <pfreixes@gmail.com>
Co-authored-by: Ethan Hur <ethan0311@gmail.com>
Co-authored-by: Jackie <18378976+Pyrodash@users.noreply.github.com>
Co-authored-by: Kristinn Björgvin Árdal <kristinnardalsecondary@gmail.com>
Co-authored-by: ffenix113 <razerer@bigmir.net>
Co-authored-by: Bastien Penavayre <bastienPenava@gmail.com>
Co-authored-by: James3 Li(李麒傑) <james3_li@asus.com>
Co-authored-by: Eng Zer Jun <engzerjun@gmail.com>
Co-authored-by: gzjiangtao2014 <gzjiangtao2014@corp.netease.com>
Co-authored-by: Nelz <nelz9999@users.noreply.github.com>
Co-authored-by: Daniel Richter <Nexyz9@gmail.com>
Co-authored-by: Seyed Ali Ghaffari <ali.ghaffari@outlook.com>
Co-authored-by: lintanghui <lintanghui@bilibili.com>
Co-authored-by: hidu <duv123+github@gmail.com>
Co-authored-by: Jonas Lergell <jonas.lergell@volvocars.com>
Co-authored-by: Alex Kahn <alexanderkahn@gmail.com>
2022-03-19 07:40:31 +03:00
|
|
|
cmd.setReadTimeout(timeout)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 14:09:23 +03:00
|
|
|
func (c cmdable) WaitAOF(ctx context.Context, numLocal, numSlaves int, timeout time.Duration) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "waitAOF", numLocal, numSlaves, int(timeout/time.Millisecond))
|
|
|
|
cmd.setReadTimeout(timeout)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c statefulCmdable) Select(ctx context.Context, index int) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "select", index)
|
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c statefulCmdable) SwapDB(ctx context.Context, index1, index2 int) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "swapdb", index1, index2)
|
|
|
|
_ = c(ctx, cmd)
|
2017-02-16 19:15:34 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
// ClientSetName assigns a name to the connection.
|
|
|
|
func (c statefulCmdable) ClientSetName(ctx context.Context, name string) *BoolCmd {
|
|
|
|
cmd := NewBoolCmd(ctx, "client", "setname", name)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
2012-08-25 16:40:49 +04:00
|
|
|
}
|
|
|
|
|
2023-09-14 16:24:23 +03:00
|
|
|
// ClientSetInfo sends a CLIENT SETINFO command with the provided info.
|
|
|
|
func (c statefulCmdable) ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd {
|
|
|
|
err := info.Validate()
|
|
|
|
if err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmd *StatusCmd
|
|
|
|
if info.LibName != nil {
|
2023-09-20 10:33:09 +03:00
|
|
|
libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, runtime.Version())
|
|
|
|
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName)
|
2023-09-14 16:24:23 +03:00
|
|
|
} else {
|
|
|
|
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer)
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2024-01-15 23:45:49 +03:00
|
|
|
type ClientTrackingOptions struct {
|
|
|
|
ClientID int
|
|
|
|
Prefixes []interface{}
|
|
|
|
BCast bool
|
|
|
|
OptIn bool
|
|
|
|
OptOut bool
|
|
|
|
NoLoop bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enables the tracking feature of the Redis server, that is used
|
|
|
|
// for server assisted client side caching.
|
|
|
|
// “on“ indicate for tracking on or tracking off. The dafualt is on.
|
|
|
|
// “clientid“ send invalidation messages to the connection with
|
|
|
|
// the specified ID.
|
|
|
|
// “bcast“ enable tracking in broadcasting mode. In this mode
|
|
|
|
// invalidation messages are reported for all the prefixes
|
|
|
|
// specified, regardless of the keys requested by the connection.
|
|
|
|
// “optin“ when broadcasting is NOT active, normally don't track
|
|
|
|
// keys in read only commands, unless they are called immediately
|
|
|
|
// after a CLIENT CACHING yes command.
|
|
|
|
// “optout“ when broadcasting is NOT active, normally track keys in
|
|
|
|
// read only commands, unless they are called immediately after a
|
|
|
|
// CLIENT CACHING no command.
|
|
|
|
// “noloop“ don't send notifications about keys modified by this
|
|
|
|
// connection itself.
|
|
|
|
// “prefixes“ for broadcasting, register a given key prefix, so that
|
|
|
|
// notifications will be provided only for keys starting with this string.
|
|
|
|
// For more innformation - https://redis.io/commands/client-tracking
|
|
|
|
func (c statefulCmdable) ClientTracking(ctx context.Context, on bool, options *ClientTrackingOptions) *StatusCmd {
|
|
|
|
args := []interface{}{"CLIENT", "TRACKING"}
|
|
|
|
if on {
|
|
|
|
args = append(args, "ON")
|
|
|
|
} else {
|
|
|
|
args = append(args, "OFF")
|
|
|
|
}
|
|
|
|
if options != nil {
|
|
|
|
if options.Prefixes != nil && !options.BCast {
|
|
|
|
panic("prefixes can only be used with BCast")
|
|
|
|
}
|
|
|
|
if options.ClientID != 0 {
|
|
|
|
args = append(args, "REDIRECT", options.ClientID)
|
|
|
|
}
|
|
|
|
for _, prefix := range options.Prefixes {
|
|
|
|
args = append(args, "PREFIX", prefix)
|
|
|
|
}
|
|
|
|
if options.BCast {
|
|
|
|
args = append(args, "BCAST")
|
|
|
|
}
|
|
|
|
if options.OptIn {
|
|
|
|
args = append(args, "OPTIN")
|
|
|
|
}
|
|
|
|
if options.OptOut {
|
|
|
|
args = append(args, "OPTOUT")
|
|
|
|
}
|
|
|
|
if options.NoLoop {
|
|
|
|
args = append(args, "NOLOOP")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c statefulCmdable) ClientTrackingOn(ctx context.Context) *StatusCmd {
|
|
|
|
return c.ClientTracking(ctx, true, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c statefulCmdable) ClientTrackingOff(ctx context.Context) *StatusCmd {
|
|
|
|
return c.ClientTracking(ctx, false, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c statefulCmdable) ClientTrackingOnWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd {
|
|
|
|
return c.ClientTracking(ctx, true, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c statefulCmdable) ClientTrackingOffWithArgs(ctx context.Context, options *ClientTrackingOptions) *StatusCmd {
|
|
|
|
return c.ClientTracking(ctx, false, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c statefulCmdable) ClientTrackingInfo(ctx context.Context) *MapStringInterfaceCmd {
|
|
|
|
cmd := NewMapStringInterfaceCmd(ctx, "CLIENT", "TRACKINGINFO")
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-14 16:24:23 +03:00
|
|
|
// Validate checks if only one field in the struct is non-nil.
|
|
|
|
func (info LibraryInfo) Validate() error {
|
|
|
|
if info.LibName != nil && info.LibVer != nil {
|
|
|
|
return errors.New("both LibName and LibVer cannot be set at the same time")
|
|
|
|
}
|
|
|
|
if info.LibName == nil && info.LibVer == nil {
|
|
|
|
return errors.New("at least one of LibName and LibVer should be set")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-01 06:21:13 +03:00
|
|
|
// Hello Set the resp protocol used.
|
2021-04-27 10:04:46 +03:00
|
|
|
func (c statefulCmdable) Hello(ctx context.Context,
|
2023-09-20 14:03:44 +03:00
|
|
|
ver int, username, password, clientName string,
|
|
|
|
) *MapStringInterfaceCmd {
|
2021-04-27 10:04:46 +03:00
|
|
|
args := make([]interface{}, 0, 7)
|
|
|
|
args = append(args, "hello", ver)
|
|
|
|
if password != "" {
|
|
|
|
if username != "" {
|
|
|
|
args = append(args, "auth", username, password)
|
|
|
|
} else {
|
|
|
|
args = append(args, "auth", "default", password)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if clientName != "" {
|
|
|
|
args = append(args, "setname", clientName)
|
|
|
|
}
|
|
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd {
|
|
|
|
cmd := NewCommandsInfoCmd(ctx, "command")
|
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-03-24 10:38:11 +03:00
|
|
|
// FilterBy is used for the `CommandList` command parameter.
|
|
|
|
type FilterBy struct {
|
|
|
|
Module string
|
|
|
|
ACLCat string
|
|
|
|
Pattern string
|
|
|
|
}
|
|
|
|
|
2023-03-19 13:20:42 +03:00
|
|
|
func (c cmdable) CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd {
|
|
|
|
args := make([]interface{}, 0, 5)
|
|
|
|
args = append(args, "command", "list")
|
|
|
|
if filter != nil {
|
|
|
|
if filter.Module != "" {
|
|
|
|
args = append(args, "filterby", "module", filter.Module)
|
|
|
|
} else if filter.ACLCat != "" {
|
|
|
|
args = append(args, "filterby", "aclcat", filter.ACLCat)
|
|
|
|
} else if filter.Pattern != "" {
|
|
|
|
args = append(args, "filterby", "pattern", filter.Pattern)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd := NewStringSliceCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-03-25 13:57:12 +03:00
|
|
|
func (c cmdable) CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd {
|
|
|
|
args := make([]interface{}, 2+len(commands))
|
|
|
|
args[0] = "command"
|
|
|
|
args[1] = "getkeys"
|
|
|
|
copy(args[2:], commands)
|
|
|
|
cmd := NewStringSliceCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c cmdable) CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd {
|
|
|
|
args := make([]interface{}, 2+len(commands))
|
|
|
|
args[0] = "command"
|
|
|
|
args[1] = "getkeysandflags"
|
|
|
|
copy(args[2:], commands)
|
|
|
|
cmd := NewKeyFlagsCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
// ClientGetName returns the name of the connection.
|
|
|
|
func (c cmdable) ClientGetName(ctx context.Context) *StringCmd {
|
|
|
|
cmd := NewStringCmd(ctx, "client", "getname")
|
|
|
|
_ = c(ctx, cmd)
|
2018-02-14 07:42:19 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd {
|
|
|
|
cmd := NewStringCmd(ctx, "echo", message)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
2012-07-26 19:16:17 +04:00
|
|
|
|
2020-03-11 17:26:42 +03:00
|
|
|
func (c cmdable) Ping(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "ping")
|
|
|
|
_ = c(ctx, cmd)
|
2018-07-22 09:46:29 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2021-06-23 13:47:10 +03:00
|
|
|
func (c cmdable) Quit(_ context.Context) *StatusCmd {
|
2020-03-11 17:26:42 +03:00
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (c cmdable) BgRewriteAOF(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "bgrewriteaof")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) BgSave(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "bgsave")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2016-12-22 14:42:05 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientKill(ctx context.Context, ipPort string) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "client", "kill", ipPort)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
// ClientKillByFilter is new style syntax, while the ClientKill is old
|
|
|
|
//
|
|
|
|
// CLIENT KILL <option> [value] ... <option> [value]
|
|
|
|
func (c cmdable) ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd {
|
|
|
|
args := make([]interface{}, 2+len(keys))
|
|
|
|
args[0] = "client"
|
|
|
|
args[1] = "kill"
|
2017-02-10 13:15:25 +03:00
|
|
|
for i, key := range keys {
|
2023-09-20 16:08:24 +03:00
|
|
|
args[2+i] = key
|
2017-02-10 13:15:25 +03:00
|
|
|
}
|
2020-03-11 17:26:42 +03:00
|
|
|
cmd := NewIntCmd(ctx, args...)
|
|
|
|
_ = c(ctx, cmd)
|
2017-02-10 13:15:25 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientList(ctx context.Context) *StringCmd {
|
|
|
|
cmd := NewStringCmd(ctx, "client", "list")
|
2023-02-07 13:41:27 +03:00
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientPause(ctx context.Context, dur time.Duration) *BoolCmd {
|
|
|
|
cmd := NewBoolCmd(ctx, "client", "pause", formatMs(ctx, dur))
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientUnpause(ctx context.Context) *BoolCmd {
|
|
|
|
cmd := NewBoolCmd(ctx, "client", "unpause")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientID(ctx context.Context) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "client", "id")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientUnblock(ctx context.Context, id int64) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "client", "unblock", id)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientUnblockWithError(ctx context.Context, id int64) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "client", "unblock", id, "error")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ClientInfo(ctx context.Context) *ClientInfoCmd {
|
|
|
|
cmd := NewClientInfoCmd(ctx, "client", "info")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
// ------------------------------------------------------------------------------------------------
|
2012-07-26 19:16:17 +04:00
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd {
|
|
|
|
cmd := NewMapStringStringCmd(ctx, "config", "get", parameter)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ConfigResetStat(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "config", "resetstat")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ConfigSet(ctx context.Context, parameter, value string) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "config", "set", parameter, value)
|
2023-02-07 13:41:27 +03:00
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ConfigRewrite(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "config", "rewrite")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) DBSize(ctx context.Context) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "dbsize")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) FlushAll(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "flushall")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) FlushAllAsync(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "flushall", "async")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) FlushDB(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "flushdb")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) FlushDBAsync(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "flushdb", "async")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2015-07-11 12:23:04 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) Info(ctx context.Context, sections ...string) *StringCmd {
|
|
|
|
args := make([]interface{}, 1+len(sections))
|
|
|
|
args[0] = "info"
|
|
|
|
for i, section := range sections {
|
|
|
|
args[i+1] = section
|
2012-08-17 22:36:48 +04:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
cmd := NewStringCmd(ctx, args...)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2016-01-22 13:29:23 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-10-30 19:07:42 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) LastSave(ctx context.Context) *IntCmd {
|
|
|
|
cmd := NewIntCmd(ctx, "lastsave")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2018-02-13 17:08:11 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) Save(ctx context.Context) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "save")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) shutdown(ctx context.Context, modifier string) *StatusCmd {
|
|
|
|
var args []interface{}
|
|
|
|
if modifier == "" {
|
|
|
|
args = []interface{}{"shutdown"}
|
|
|
|
} else {
|
|
|
|
args = []interface{}{"shutdown", modifier}
|
2018-02-14 07:42:19 +03:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
cmd := NewStatusCmd(ctx, args...)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2023-09-20 16:08:24 +03:00
|
|
|
if err := cmd.Err(); err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
// Server quit as expected.
|
|
|
|
cmd.err = nil
|
2021-03-27 05:37:44 +03:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
} else {
|
|
|
|
// Server did not quit. String reply contains the reason.
|
|
|
|
cmd.err = errors.New(cmd.val)
|
|
|
|
cmd.val = ""
|
2021-03-27 05:37:44 +03:00
|
|
|
}
|
2021-03-26 12:13:22 +03:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) Shutdown(ctx context.Context) *StatusCmd {
|
|
|
|
return c.shutdown(ctx, "")
|
2021-03-26 12:13:22 +03:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ShutdownSave(ctx context.Context) *StatusCmd {
|
|
|
|
return c.shutdown(ctx, "save")
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) ShutdownNoSave(ctx context.Context) *StatusCmd {
|
|
|
|
return c.shutdown(ctx, "nosave")
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) SlaveOf(ctx context.Context, host, port string) *StatusCmd {
|
|
|
|
cmd := NewStatusCmd(ctx, "slaveof", host, port)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) SlowLogGet(ctx context.Context, num int64) *SlowLogCmd {
|
|
|
|
cmd := NewSlowLogCmd(context.Background(), "slowlog", "get", num)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) Sync(_ context.Context) {
|
|
|
|
panic("not implemented")
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) Time(ctx context.Context) *TimeCmd {
|
|
|
|
cmd := NewTimeCmd(ctx, "time")
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-26 19:16:17 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) DebugObject(ctx context.Context, key string) *StringCmd {
|
|
|
|
cmd := NewStringCmd(ctx, "debug", "object", key)
|
2020-03-11 17:26:42 +03:00
|
|
|
_ = c(ctx, cmd)
|
2014-06-25 11:40:56 +04:00
|
|
|
return cmd
|
2012-07-25 17:00:50 +04:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c cmdable) MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd {
|
|
|
|
args := []interface{}{"memory", "usage", key}
|
|
|
|
if len(samples) > 0 {
|
|
|
|
if len(samples) != 1 {
|
|
|
|
panic("MemoryUsage expects single sample count")
|
2021-02-17 15:48:47 +03:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
args = append(args, "SAMPLES", samples[0])
|
2021-02-17 15:48:47 +03:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
cmd := NewIntCmd(ctx, args...)
|
|
|
|
cmd.SetFirstKeyPos(2)
|
2021-02-17 15:48:47 +03:00
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ModuleLoadexConfig struct is used to specify the arguments for the MODULE LOADEX command of redis.
|
|
|
|
// `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]`
|
|
|
|
type ModuleLoadexConfig struct {
|
|
|
|
Path string
|
|
|
|
Conf map[string]interface{}
|
|
|
|
Args []interface{}
|
2020-10-22 21:38:36 +03:00
|
|
|
}
|
|
|
|
|
2023-09-20 16:08:24 +03:00
|
|
|
func (c *ModuleLoadexConfig) toArgs() []interface{} {
|
|
|
|
args := make([]interface{}, 3, 3+len(c.Conf)*3+len(c.Args)*2)
|
|
|
|
args[0] = "MODULE"
|
|
|
|
args[1] = "LOADEX"
|
|
|
|
args[2] = c.Path
|
|
|
|
for k, v := range c.Conf {
|
|
|
|
args = append(args, "CONFIG", k, v)
|
2015-01-31 12:08:56 +03:00
|
|
|
}
|
2023-09-20 16:08:24 +03:00
|
|
|
for _, arg := range c.Args {
|
|
|
|
args = append(args, "ARGS", arg)
|
|
|
|
}
|
|
|
|
return args
|
2023-04-18 16:03:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ModuleLoadex Redis `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]` command.
|
|
|
|
func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd {
|
|
|
|
cmd := NewStringCmd(ctx, conf.toArgs()...)
|
|
|
|
_ = c(ctx, cmd)
|
|
|
|
return cmd
|
|
|
|
}
|
2023-12-17 13:35:10 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
|
|
|
}
|