redis/commands.go

810 lines
22 KiB
Go
Raw Normal View History

2012-07-25 17:00:50 +04:00
package redis
import (
2020-03-11 17:26:42 +03:00
"context"
"encoding"
2018-02-22 15:14:30 +03:00
"errors"
"fmt"
2014-11-13 15:26:14 +03:00
"io"
"net"
2021-12-17 05:54:43 +03:00
"reflect"
"runtime"
"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
)
// 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.
// For example:
//
2022-10-07 22:40:51 +03:00
// rdb.Set(ctx, key, value, redis.KeepTTL)
const KeepTTL = -1
func usePrecise(dur time.Duration) bool {
return dur < time.Second || dur%time.Second != 0
}
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(
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,
)
return 1
2015-04-07 12:42:16 +03:00
}
2017-03-24 13:48:32 +03:00
return int64(dur / time.Millisecond)
}
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(
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,
)
return 1
2015-04-07 12:42:16 +03:00
}
2017-03-24 13:48:32 +03:00
return int64(dur / time.Second)
}
func appendArgs(dst, src []interface{}) []interface{} {
if len(src) == 1 {
2020-06-29 17:48:57 +03:00
return appendArg(dst, src[0])
}
2019-07-25 13:28:15 +03:00
dst = append(dst, src...)
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
case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP:
return append(dst, arg)
2020-06-29 17:48:57 +03:00
default:
// 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)
}
}
// 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
}
name, opt, _ := strings.Cut(tag, ",")
if name == "" {
continue
}
2021-12-17 05:54:43 +03:00
field := v.Field(i)
// miss field
if omitEmpty(opt) && isEmptyValue(field) {
continue
}
if field.CanInterface() {
dst = append(dst, name, field.Interface())
2021-12-17 05:54:43 +03:00
}
}
return dst
2021-12-17 05:54:43 +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
}
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
CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd
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
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
ClientUnblock(ctx context.Context, id int64) *IntCmd
ClientUnblockWithError(ctx context.Context, id int64) *IntCmd
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
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
MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
2023-08-15 10:19:19 +03:00
ACLCmdable
BitMapCmdable
ClusterCmdable
GearsCmdable
GenericCmdable
GeoCmdable
HashCmdable
HyperLogLogCmdable
ListCmdable
ProbabilisticCmdable
PubSubCmdable
ScriptingFunctionsCmdable
SetCmdable
SortedSetCmdable
StringCmdable
Json support (#2769) * Add support for RedisJSON * Add optional args and tests * Add more tests * Add more tests * Add more tests and cleanups * Add docstring * update JSONArrIndex and matching texts Rename JSONArrIndexWithArgs to JSONArrIndexArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONArrTrim and matching texts Rename JSONArrTrimWithArgs to JSONArrTrimArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONGetWithArgs and matching texts Rename JSONGetWithArgs to JSONGetArgs Removed Paths & Path from args - redundant and ambiguous Renamed mispelled "Indention" Changed args param from pointer to value Updated and added additional tests for formatting params * Update JSONSetMode changed mode parameter back to string, added value checking for that string * Updated JSONMSet Changed name of param struct to ...Args for consistency Updated arg list to use an array of structs not pointers as all args are mandatory (we never want to pass a nil parameter) * Updated tests for JSONMSet * Added stubbed (panicking) implementations of JSON.RESP AND JSON.DEBUG * Pre-pull request tidy up Renamed xArgs() to xWithArgs to match other Redis modules Modified params to xWithArgs functions to use a pointer for the arguments struct to match other Redis modules. Modified JSONMSet to JSONMSetArgs and added a version that takes a vararray of args to match the semantics of the native redis call Updated tests to match above changes. Renamed some variables in the json tests for consistency * Testing fixes Fixed error in JSONSetWithArgs found during tests Fixed tests for indentation * fix JSONCmdable typo (#5) * Remove deprecated command: JSONNumMultBy (#6) * Lowercase newJSONCmd (#7) --------- Co-authored-by: Nic Gibson <nic.gibson@redis.com> Co-authored-by: Nic Gibson <newt+github@noslogan.org>
2023-10-30 15:09:25 +03:00
StreamCmdable
TimeseriesCmdable
Json support (#2769) * Add support for RedisJSON * Add optional args and tests * Add more tests * Add more tests * Add more tests and cleanups * Add docstring * update JSONArrIndex and matching texts Rename JSONArrIndexWithArgs to JSONArrIndexArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONArrTrim and matching texts Rename JSONArrTrimWithArgs to JSONArrTrimArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONGetWithArgs and matching texts Rename JSONGetWithArgs to JSONGetArgs Removed Paths & Path from args - redundant and ambiguous Renamed mispelled "Indention" Changed args param from pointer to value Updated and added additional tests for formatting params * Update JSONSetMode changed mode parameter back to string, added value checking for that string * Updated JSONMSet Changed name of param struct to ...Args for consistency Updated arg list to use an array of structs not pointers as all args are mandatory (we never want to pass a nil parameter) * Updated tests for JSONMSet * Added stubbed (panicking) implementations of JSON.RESP AND JSON.DEBUG * Pre-pull request tidy up Renamed xArgs() to xWithArgs to match other Redis modules Modified params to xWithArgs functions to use a pointer for the arguments struct to match other Redis modules. Modified JSONMSet to JSONMSetArgs and added a version that takes a vararray of args to match the semantics of the native redis call Updated tests to match above changes. Renamed some variables in the json tests for consistency * Testing fixes Fixed error in JSONSetWithArgs found during tests Fixed tests for indentation * fix JSONCmdable typo (#5) * Remove deprecated command: JSONNumMultBy (#6) * Lowercase newJSONCmd (#7) --------- Co-authored-by: Nic Gibson <nic.gibson@redis.com> Co-authored-by: Nic Gibson <newt+github@noslogan.org>
2023-10-30 15:09:25 +03:00
JSONCmdable
}
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
ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd
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
Hello(ctx context.Context, ver int, username, password, clientName string) *MapStringInterfaceCmd
}
2020-07-16 09:52:07 +03:00
var (
_ Cmdable = (*Client)(nil)
_ Cmdable = (*Tx)(nil)
_ Cmdable = (*Ring)(nil)
_ Cmdable = (*ClusterClient)(nil)
)
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
}
// 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
}
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)
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
}
// 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 {
libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, runtime.Version())
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName)
} else {
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer)
}
_ = c(ctx, cmd)
return cmd
}
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
}
// 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
}
// Hello Set the resp protocol used.
func (c statefulCmdable) Hello(ctx context.Context,
ver int, username, password, clientName string,
) *MapStringInterfaceCmd {
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
}
// FilterBy is used for the `CommandList` command parameter.
type FilterBy struct {
Module string
ACLCat string
Pattern string
}
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
}
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
}
func (c cmdable) Quit(_ context.Context) *StatusCmd {
2020-03-11 17:26:42 +03:00
panic("not implemented")
}
//------------------------------------------------------------------------------
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
}
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
}
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
}
// 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 {
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
}
func (c cmdable) ClientList(ctx context.Context) *StringCmd {
cmd := NewStringCmd(ctx, "client", "list")
_ = c(ctx, cmd)
return cmd
}
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
}
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
}
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
}
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
}
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
}
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
}
// ------------------------------------------------------------------------------------------------
2012-07-26 19:16:17 +04: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
}
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
}
func (c cmdable) ConfigSet(ctx context.Context, parameter, value string) *StatusCmd {
cmd := NewStatusCmd(ctx, "config", "set", parameter, value)
_ = c(ctx, cmd)
return cmd
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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")
2020-03-11 17:26:42 +03:00
_ = c(ctx, cmd)
2018-02-13 17:08:11 +03:00
return cmd
}
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
}
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
}
cmd := NewStatusCmd(ctx, args...)
2020-03-11 17:26:42 +03:00
_ = c(ctx, cmd)
if err := cmd.Err(); err != nil {
if err == io.EOF {
// Server quit as expected.
cmd.err = nil
}
} else {
// Server did not quit. String reply contains the reason.
cmd.err = errors.New(cmd.val)
cmd.val = ""
}
return cmd
}
func (c cmdable) Shutdown(ctx context.Context) *StatusCmd {
return c.shutdown(ctx, "")
}
func (c cmdable) ShutdownSave(ctx context.Context) *StatusCmd {
return c.shutdown(ctx, "save")
2012-07-26 19:16:17 +04:00
}
func (c cmdable) ShutdownNoSave(ctx context.Context) *StatusCmd {
return c.shutdown(ctx, "nosave")
2012-07-26 19:16:17 +04: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
}
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
}
func (c cmdable) Sync(_ context.Context) {
panic("not implemented")
2012-07-26 19:16:17 +04: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
}
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
}
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")
}
args = append(args, "SAMPLES", samples[0])
}
cmd := NewIntCmd(ctx, args...)
cmd.SetFirstKeyPos(2)
_ = c(ctx, cmd)
return cmd
}
//------------------------------------------------------------------------------
// 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
}
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)
}
for _, arg := range c.Args {
args = append(args, "ARGS", arg)
}
return args
}
// 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
}
/*
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
}