diff --git a/.dockerignore b/.dockerignore index f3b64113..af9645a7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,7 @@ **/.git +.dockerignore +Dockerfile +goctl +Makefile +readme.md +readme-cn.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a687e57b..c9454581 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,14 @@ version: 2 updates: + - package-ecosystem: "docker" # Update image tags in Dockerfile + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" # Update GitHub Actions + directory: "/" + schedule: + interval: "weekly" - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests schedule: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2b1ec376..cfb233b7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,7 +39,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +50,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +64,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f3a3d561..d2f5af0f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -15,9 +15,9 @@ jobs: uses: actions/checkout@v4 - name: Set up Go 1.x - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: '1.19' check-latest: true cache: true id: go @@ -40,7 +40,7 @@ jobs: run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... - name: Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 test-win: name: Windows @@ -50,10 +50,10 @@ jobs: uses: actions/checkout@v4 - name: Set up Go 1.x - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: # use 1.19 to guarantee Go 1.19 compatibility - go-version: 1.19 + go-version: '1.19' check-latest: true cache: true diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index b7727567..5b2d9a28 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -7,7 +7,7 @@ jobs: close-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: days-before-issue-stale: 365 days-before-issue-close: 90 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..196c0453 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +We publish releases monthly. + +| Version | Supported | +| ------- | ------------------ | +| >= 1.4.4 | :white_check_mark: | +| < 1.4.4 | :x: | + +## Reporting a Vulnerability + +https://github.com/zeromicro/go-zero/security/advisories + +Accepted vulnerabilities are expected to be fixed within a month. diff --git a/core/bloom/bloom.go b/core/bloom/bloom.go index 897f4bc8..3a72cf84 100644 --- a/core/bloom/bloom.go +++ b/core/bloom/bloom.go @@ -130,7 +130,7 @@ func (r *redisBitSet) check(ctx context.Context, offsets []uint) (bool, error) { } resp, err := r.store.ScriptRunCtx(ctx, testScript, []string{r.key}, args) - if err == redis.Nil { + if errors.Is(err, redis.Nil) { return false, nil } else if err != nil { return false, err @@ -162,7 +162,7 @@ func (r *redisBitSet) set(ctx context.Context, offsets []uint) error { } _, err = r.store.ScriptRunCtx(ctx, setScript, []string{r.key}, args) - if err == redis.Nil { + if errors.Is(err, redis.Nil) { return nil } diff --git a/core/breaker/breakers.go b/core/breaker/breakers.go index 314965c4..1df42d3e 100644 --- a/core/breaker/breakers.go +++ b/core/breaker/breakers.go @@ -59,7 +59,7 @@ func GetBreaker(name string) Breaker { // NoBreakerFor disables the circuit breaker for the given name. func NoBreakerFor(name string) { lock.Lock() - breakers[name] = newNopBreaker() + breakers[name] = NopBreaker() lock.Unlock() } diff --git a/core/breaker/nopbreaker.go b/core/breaker/nopbreaker.go index cd030b63..baa09801 100644 --- a/core/breaker/nopbreaker.go +++ b/core/breaker/nopbreaker.go @@ -4,7 +4,8 @@ const nopBreakerName = "nopBreaker" type nopBreaker struct{} -func newNopBreaker() Breaker { +// NopBreaker returns a breaker that never trigger breaker circuit. +func NopBreaker() Breaker { return nopBreaker{} } diff --git a/core/breaker/nopbreaker_test.go b/core/breaker/nopbreaker_test.go index 1756aa2a..ac26428d 100644 --- a/core/breaker/nopbreaker_test.go +++ b/core/breaker/nopbreaker_test.go @@ -8,7 +8,7 @@ import ( ) func TestNopBreaker(t *testing.T) { - b := newNopBreaker() + b := NopBreaker() assert.Equal(t, nopBreakerName, b.Name()) p, err := b.Allow() assert.Nil(t, err) diff --git a/core/codec/dh.go b/core/codec/dh.go index 49d839b4..63dd2d9c 100644 --- a/core/codec/dh.go +++ b/core/codec/dh.go @@ -23,7 +23,7 @@ var ( zero = big.NewInt(0) ) -// DhKey defines the Diffie Hellman key. +// DhKey defines the Diffie-Hellman key. type DhKey struct { PriKey *big.Int PubKey *big.Int @@ -46,7 +46,7 @@ func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) { return new(big.Int).Exp(pubKey, priKey, p), nil } -// GenerateKey returns a Diffie Hellman key. +// GenerateKey returns a Diffie-Hellman key. func GenerateKey() (*DhKey, error) { var err error var x *big.Int diff --git a/core/collection/cache.go b/core/collection/cache.go index ace21ece..56b31811 100644 --- a/core/collection/cache.go +++ b/core/collection/cache.go @@ -128,8 +128,8 @@ func (c *Cache) Take(key string, fetch func() (any, error)) (any, error) { var fresh bool val, err := c.barrier.Do(key, func() (any, error) { - // because O(1) on map search in memory, and fetch is an IO query - // so we do double check, cache might be taken by another call + // because O(1) on map search in memory, and fetch is an IO query, + // so we do double-check, cache might be taken by another call if val, ok := c.doGet(key); ok { return val, nil } diff --git a/core/conf/config.go b/core/conf/config.go index 64796ef7..d3795c0a 100644 --- a/core/conf/config.go +++ b/core/conf/config.go @@ -133,7 +133,7 @@ func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo, fullName st return newConflictKeyError(fullName) } - if err := mergeFields(prev, key, child.children, fullName); err != nil { + if err := mergeFields(prev, child.children, fullName); err != nil { return err } } else { @@ -281,7 +281,7 @@ func getTagName(field reflect.StructField) string { return field.Name } -func mergeFields(prev *fieldInfo, key string, children map[string]*fieldInfo, fullName string) error { +func mergeFields(prev *fieldInfo, children map[string]*fieldInfo, fullName string) error { if len(prev.children) == 0 || len(children) == 0 { return newConflictKeyError(fullName) } diff --git a/core/discov/internal/registry.go b/core/discov/internal/registry.go index b354bb2b..462906fc 100644 --- a/core/discov/internal/registry.go +++ b/core/discov/internal/registry.go @@ -222,7 +222,7 @@ func (c *cluster) load(cli EtcdClient, key string) int64 { break } - logx.Error(err) + logx.Errorf("%s, key is %s", err.Error(), key) time.Sleep(coolDownInterval) } diff --git a/core/errorx/batcherror.go b/core/errorx/batcherror.go index 92ae644d..e0fcbb27 100644 --- a/core/errorx/batcherror.go +++ b/core/errorx/batcherror.go @@ -1,11 +1,15 @@ package errorx -import "bytes" +import ( + "bytes" + "sync" +) type ( // A BatchError is an error that can hold multiple errors. BatchError struct { errs errorArray + lock sync.Mutex } errorArray []error @@ -13,6 +17,9 @@ type ( // Add adds errs to be, nil errors are ignored. func (be *BatchError) Add(errs ...error) { + be.lock.Lock() + defer be.lock.Unlock() + for _, err := range errs { if err != nil { be.errs = append(be.errs, err) @@ -22,6 +29,9 @@ func (be *BatchError) Add(errs ...error) { // Err returns an error that represents all errors. func (be *BatchError) Err() error { + be.lock.Lock() + defer be.lock.Unlock() + switch len(be.errs) { case 0: return nil @@ -34,6 +44,9 @@ func (be *BatchError) Err() error { // NotNil checks if any error inside. func (be *BatchError) NotNil() bool { + be.lock.Lock() + defer be.lock.Unlock() + return len(be.errs) > 0 } diff --git a/core/errorx/batcherror_test.go b/core/errorx/batcherror_test.go index ae5c8c3e..3e345343 100644 --- a/core/errorx/batcherror_test.go +++ b/core/errorx/batcherror_test.go @@ -3,6 +3,7 @@ package errorx import ( "errors" "fmt" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -33,7 +34,7 @@ func TestBatchErrorNilFromFunc(t *testing.T) { func TestBatchErrorOneError(t *testing.T) { var batch BatchError batch.Add(errors.New(err1)) - assert.NotNil(t, batch) + assert.NotNil(t, batch.Err()) assert.Equal(t, err1, batch.Err().Error()) assert.True(t, batch.NotNil()) } @@ -42,7 +43,26 @@ func TestBatchErrorWithErrors(t *testing.T) { var batch BatchError batch.Add(errors.New(err1)) batch.Add(errors.New(err2)) - assert.NotNil(t, batch) + assert.NotNil(t, batch.Err()) assert.Equal(t, fmt.Sprintf("%s\n%s", err1, err2), batch.Err().Error()) assert.True(t, batch.NotNil()) } + +func TestBatchErrorConcurrentAdd(t *testing.T) { + const count = 10000 + var batch BatchError + var wg sync.WaitGroup + + wg.Add(count) + for i := 0; i < count; i++ { + go func() { + defer wg.Done() + batch.Add(errors.New(err1)) + }() + } + wg.Wait() + + assert.NotNil(t, batch.Err()) + assert.Equal(t, count, len(batch.errs)) + assert.True(t, batch.NotNil()) +} diff --git a/core/filex/progressscanner.go b/core/filex/progressscanner.go index 11f3c1a5..e4f187f6 100644 --- a/core/filex/progressscanner.go +++ b/core/filex/progressscanner.go @@ -5,7 +5,7 @@ import "gopkg.in/cheggaaa/pb.v1" type ( // A Scanner is used to read lines. Scanner interface { - // Scan checks if has remaining to read. + // Scan checks if it has remaining to read. Scan() bool // Text returns next line. Text() string diff --git a/core/fx/retry.go b/core/fx/retry.go index e79c9002..836564c7 100644 --- a/core/fx/retry.go +++ b/core/fx/retry.go @@ -2,6 +2,7 @@ package fx import ( "context" + "errors" "time" "github.com/zeromicro/go-zero/core/errorx" @@ -14,9 +15,10 @@ type ( RetryOption func(*retryOptions) retryOptions struct { - times int - interval time.Duration - timeout time.Duration + times int + interval time.Duration + timeout time.Duration + ignoreErrors []error } ) @@ -62,6 +64,11 @@ func retry(ctx context.Context, fn func(errChan chan error, retryCount int), opt select { case err := <-errChan: if err != nil { + for _, ignoreErr := range options.ignoreErrors { + if errors.Is(err, ignoreErr) { + return nil + } + } berr.Add(err) } else { return nil @@ -84,19 +91,28 @@ func retry(ctx context.Context, fn func(errChan chan error, retryCount int), opt return berr.Err() } -// WithRetry customize a DoWithRetry call with given retry times. -func WithRetry(times int) RetryOption { +// WithIgnoreErrors Ignore the specified errors +func WithIgnoreErrors(ignoreErrors []error) RetryOption { return func(options *retryOptions) { - options.times = times + options.ignoreErrors = ignoreErrors } } +// WithInterval customizes a DoWithRetry call with given interval. func WithInterval(interval time.Duration) RetryOption { return func(options *retryOptions) { options.interval = interval } } +// WithRetry customizes a DoWithRetry call with given retry times. +func WithRetry(times int) RetryOption { + return func(options *retryOptions) { + options.times = times + } +} + +// WithTimeout customizes a DoWithRetry call with given timeout. func WithTimeout(timeout time.Duration) RetryOption { return func(options *retryOptions) { options.timeout = timeout diff --git a/core/fx/retry_test.go b/core/fx/retry_test.go index 045d782a..5a8d06b6 100644 --- a/core/fx/retry_test.go +++ b/core/fx/retry_test.go @@ -97,6 +97,24 @@ func TestRetryWithInterval(t *testing.T) { } +func TestRetryWithWithIgnoreErrors(t *testing.T) { + ignoreErr1 := errors.New("ignore error1") + ignoreErr2 := errors.New("ignore error2") + ignoreErrs := []error{ignoreErr1, ignoreErr2} + + assert.Nil(t, DoWithRetry(func() error { + return ignoreErr1 + }, WithIgnoreErrors(ignoreErrs))) + + assert.Nil(t, DoWithRetry(func() error { + return ignoreErr2 + }, WithIgnoreErrors(ignoreErrs))) + + assert.NotNil(t, DoWithRetry(func() error { + return errors.New("any") + })) +} + func TestRetryCtx(t *testing.T) { t.Run("with timeout", func(t *testing.T) { assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error { diff --git a/core/fx/stream.go b/core/fx/stream.go index cebb11e3..e0c3cd28 100644 --- a/core/fx/stream.go +++ b/core/fx/stream.go @@ -352,7 +352,7 @@ func (s Stream) Parallel(fn ParallelFunc, opts ...Option) { }, opts...).Done() } -// Reduce is an utility method to let the caller deal with the underlying channel. +// Reduce is a utility method to let the caller deal with the underlying channel. func (s Stream) Reduce(fn ReduceFunc) (any, error) { return fn(s.source) } diff --git a/core/iox/pipe.go b/core/iox/pipe.go index be36479c..044de380 100644 --- a/core/iox/pipe.go +++ b/core/iox/pipe.go @@ -2,7 +2,7 @@ package iox import "os" -// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterwards. +// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterward. func RedirectInOut() (restore func(), err error) { var r, w *os.File r, w, err = os.Pipe() diff --git a/core/iox/textfile.go b/core/iox/textfile.go index 89083cea..96309716 100644 --- a/core/iox/textfile.go +++ b/core/iox/textfile.go @@ -9,7 +9,7 @@ import ( const bufSize = 32 * 1024 -// CountLines returns the number of lines in file. +// CountLines returns the number of lines in the file. func CountLines(file string) (int, error) { f, err := os.Open(file) if err != nil { diff --git a/core/iox/textlinescanner.go b/core/iox/textlinescanner.go index ec28e27a..9f20498f 100644 --- a/core/iox/textlinescanner.go +++ b/core/iox/textlinescanner.go @@ -2,11 +2,12 @@ package iox import ( "bufio" + "errors" "io" "strings" ) -// A TextLineScanner is a scanner that can scan lines from given reader. +// A TextLineScanner is a scanner that can scan lines from the given reader. type TextLineScanner struct { reader *bufio.Reader hasNext bool @@ -14,7 +15,7 @@ type TextLineScanner struct { err error } -// NewTextLineScanner returns a TextLineScanner with given reader. +// NewTextLineScanner returns a TextLineScanner with the given reader. func NewTextLineScanner(reader io.Reader) *TextLineScanner { return &TextLineScanner{ reader: bufio.NewReader(reader), @@ -30,7 +31,7 @@ func (scanner *TextLineScanner) Scan() bool { line, err := scanner.reader.ReadString('\n') scanner.line = strings.TrimRight(line, "\n") - if err == io.EOF { + if errors.Is(err, io.EOF) { scanner.hasNext = false return true } else if err != nil { diff --git a/core/limit/tokenlimit.go b/core/limit/tokenlimit.go index fe576dd9..e33b4b95 100644 --- a/core/limit/tokenlimit.go +++ b/core/limit/tokenlimit.go @@ -125,7 +125,7 @@ func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) boo }) // redis allowed == false // Lua boolean false -> r Nil bulk reply - if err == redis.Nil { + if errors.Is(err, redis.Nil) { return false } if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { diff --git a/core/load/adaptiveshedder.go b/core/load/adaptiveshedder.go index a7b6f49d..2afc43a2 100644 --- a/core/load/adaptiveshedder.go +++ b/core/load/adaptiveshedder.go @@ -138,10 +138,10 @@ func (as *adaptiveShedder) Allow() (Promise, error) { func (as *adaptiveShedder) addFlying(delta int64) { flying := atomic.AddInt64(&as.flying, delta) // update avgFlying when the request is finished. - // this strategy makes avgFlying have a little bit lag against flying, and smoother. + // this strategy makes avgFlying have a little bit of lag against flying, and smoother. // when the flying requests increase rapidly, avgFlying increase slower, accept more requests. // when the flying requests drop rapidly, avgFlying drop slower, accept fewer requests. - // it makes the service to serve as more requests as possible. + // it makes the service to serve as many requests as possible. if delta < 0 { as.avgFlyingLock.Lock() as.avgFlying = as.avgFlying*flyingBeta + float64(flying)*(1-flyingBeta) @@ -200,7 +200,7 @@ func (as *adaptiveShedder) minRt() float64 { func (as *adaptiveShedder) overloadFactor() float64 { // as.cpuThreshold must be less than cpuMax factor := (cpuMax - float64(stat.CpuUsage())) / (cpuMax - float64(as.cpuThreshold)) - // at least accept 10% of acceptable requests even cpu is highly overloaded. + // at least accept 10% of acceptable requests, even cpu is highly overloaded. return mathx.Between(factor, overloadFactorLowerBound, 1) } @@ -250,14 +250,14 @@ func (as *adaptiveShedder) systemOverloaded() bool { return true } -// WithBuckets customizes the Shedder with given number of buckets. +// WithBuckets customizes the Shedder with the given number of buckets. func WithBuckets(buckets int) ShedderOption { return func(opts *shedderOptions) { opts.buckets = buckets } } -// WithCpuThreshold customizes the Shedder with given cpu threshold. +// WithCpuThreshold customizes the Shedder with the given cpu threshold. func WithCpuThreshold(threshold int64) ShedderOption { return func(opts *shedderOptions) { opts.cpuThreshold = threshold diff --git a/core/load/sheddergroup.go b/core/load/sheddergroup.go index c3d62402..8cc742f5 100644 --- a/core/load/sheddergroup.go +++ b/core/load/sheddergroup.go @@ -6,7 +6,7 @@ import ( "github.com/zeromicro/go-zero/core/syncx" ) -// A ShedderGroup is a manager to manage key based shedders. +// A ShedderGroup is a manager to manage key-based shedders. type ShedderGroup struct { options []ShedderOption manager *syncx.ResourceManager diff --git a/core/logc/logs.go b/core/logc/logs.go index 71233545..b8343fa2 100644 --- a/core/logc/logs.go +++ b/core/logc/logs.go @@ -42,7 +42,7 @@ func Debugv(ctx context.Context, v interface{}) { getLogger(ctx).Debugv(v) } -// Debugw writes msg along with fields into access log. +// Debugw writes msg along with fields into the access log. func Debugw(ctx context.Context, msg string, fields ...LogField) { getLogger(ctx).Debugw(msg, fields...) } @@ -63,7 +63,7 @@ func Errorv(ctx context.Context, v any) { getLogger(ctx).Errorv(v) } -// Errorw writes msg along with fields into error log. +// Errorw writes msg along with fields into the error log. func Errorw(ctx context.Context, msg string, fields ...LogField) { getLogger(ctx).Errorw(msg, fields...) } @@ -88,7 +88,7 @@ func Infov(ctx context.Context, v any) { getLogger(ctx).Infov(v) } -// Infow writes msg along with fields into access log. +// Infow writes msg along with fields into the access log. func Infow(ctx context.Context, msg string, fields ...LogField) { getLogger(ctx).Infow(msg, fields...) } @@ -108,10 +108,11 @@ func SetLevel(level uint32) { logx.SetLevel(level) } -// SetUp sets up the logx. If already set up, just return nil. -// we allow SetUp to be called multiple times, because for example +// SetUp sets up the logx. +// If already set up, return nil. +// We allow SetUp to be called multiple times, because, for example, // we need to allow different service frameworks to initialize logx respectively. -// the same logic for SetUp +// The same logic for SetUp func SetUp(c LogConf) error { return logx.SetUp(c) } diff --git a/core/logx/lesslogger.go b/core/logx/lesslogger.go index 35ca2051..04113929 100644 --- a/core/logx/lesslogger.go +++ b/core/logx/lesslogger.go @@ -1,6 +1,6 @@ package logx -// A LessLogger is a logger that control to log once during the given duration. +// A LessLogger is a logger that controls to log once during the given duration. type LessLogger struct { *limitedExecutor } diff --git a/core/logx/logs.go b/core/logx/logs.go index 0c049324..780093eb 100644 --- a/core/logx/logs.go +++ b/core/logx/logs.go @@ -86,7 +86,7 @@ func Debugv(v any) { } } -// Debugw writes msg along with fields into access log. +// Debugw writes msg along with fields into the access log. func Debugw(msg string, fields ...LogField) { if shallLog(DebugLevel) { writeDebug(msg, fields...) @@ -142,7 +142,7 @@ func Errorv(v any) { } } -// Errorw writes msg along with fields into error log. +// Errorw writes msg along with fields into the error log. func Errorw(msg string, fields ...LogField) { if shallLog(ErrorLevel) { writeError(msg, fields...) @@ -208,7 +208,7 @@ func Infov(v any) { } } -// Infow writes msg along with fields into access log. +// Infow writes msg along with fields into the access log. func Infow(msg string, fields ...LogField) { if shallLog(InfoLevel) { writeInfo(msg, fields...) @@ -254,11 +254,12 @@ func SetWriter(w Writer) { } } -// SetUp sets up the logx. If already set up, just return nil. -// we allow SetUp to be called multiple times, because for example +// SetUp sets up the logx. +// If already set up, return nil. +// We allow SetUp to be called multiple times, because, for example, // we need to allow different service frameworks to initialize logx respectively. func SetUp(c LogConf) (err error) { - // Just ignore the subsequent SetUp calls. + // Ignore the later SetUp calls. // Because multiple services in one process might call SetUp respectively. // Need to wait for the first caller to complete the execution. setupOnce.Do(func() { @@ -480,7 +481,7 @@ func writeDebug(val any, fields ...LogField) { getWriter().Debug(val, addCaller(fields...)...) } -// writeError writes v into error log. +// writeError writes v into the error log. // Not checking shallLog here is for performance consideration. // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. @@ -520,7 +521,7 @@ func writeStack(msg string) { getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack()))) } -// writeStat writes v into stat log. +// writeStat writes v into the stat log. // Not checking shallLog here is for performance consideration. // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. diff --git a/core/logx/logs_test.go b/core/logx/logs_test.go index 818f459e..3780bc2f 100644 --- a/core/logx/logs_test.go +++ b/core/logx/logs_test.go @@ -570,7 +570,7 @@ func TestErrorfWithWrappedError(t *testing.T) { old := writer.Swap(w) defer writer.Store(old) - Errorf("hello %w", errors.New(message)) + Errorf("hello %s", errors.New(message)) assert.True(t, strings.Contains(w.String(), "hello there")) } diff --git a/core/logx/rotatelogger.go b/core/logx/rotatelogger.go index 4b7f31e7..61f9a9d0 100644 --- a/core/logx/rotatelogger.go +++ b/core/logx/rotatelogger.go @@ -319,7 +319,7 @@ func (l *RotateLogger) maybeCompressFile(file string) { }() if _, err := os.Stat(file); err != nil { - // file not exists or other error, ignore compression + // file doesn't exist or another error, ignore compression return } diff --git a/core/mapping/marshaler.go b/core/mapping/marshaler.go index c54ae747..fbe31a92 100644 --- a/core/mapping/marshaler.go +++ b/core/mapping/marshaler.go @@ -12,7 +12,7 @@ const ( ) // Marshal marshals the given val and returns the map that contains the fields. -// optional=another is not implemented, and it's hard to implement and not common used. +// optional=another is not implemented, and it's hard to implement and not commonly used. func Marshal(val any) (map[string]map[string]any, error) { ret := make(map[string]map[string]any) tp := reflect.TypeOf(val) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index 1e1f2e67..12cbe46b 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -39,7 +39,7 @@ var ( ) type ( - // Unmarshaler is used to unmarshal with given tag key. + // Unmarshaler is used to unmarshal with the given tag key. Unmarshaler struct { key string opts unmarshalOptions @@ -69,7 +69,7 @@ func NewUnmarshaler(key string, opts ...UnmarshalOption) *Unmarshaler { return &unmarshaler } -// UnmarshalKey unmarshals m into v with tag key. +// UnmarshalKey unmarshals m into v with the tag key. func UnmarshalKey(m map[string]any, v any) error { return keyUnmarshaler.Unmarshal(m, v) } @@ -223,11 +223,11 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect. switch v := mapValue.(type) { case fmt.Stringer: if err := jsonx.UnmarshalFromString(v.String(), &slice); err != nil { - return err + return fmt.Errorf("fullName: `%s`, error: `%w`", fullName, err) } case string: if err := jsonx.UnmarshalFromString(v, &slice); err != nil { - return err + return fmt.Errorf("fullName: `%s`, error: `%w`", fullName, err) } default: return errUnsupportedType @@ -428,6 +428,10 @@ func (u *Unmarshaler) parseOptionsWithContext(field reflect.StructField, m Value } } + if u.opts.fillDefault { + return key, &options.fieldOptionsWithContext, nil + } + optsWithContext, err := options.toOptionsWithContext(key, m, fullName) if err != nil { return "", nil, err @@ -625,7 +629,7 @@ func (u *Unmarshaler) processFieldPrimitiveWithJSONNumber(fieldType reflect.Type return err } - // if value is a pointer, we need to check overflow with the pointer's value. + // if the value is a pointer, we need to check overflow with the pointer's value. derefedValue := value for derefedValue.Type().Kind() == reflect.Ptr { derefedValue = derefedValue.Elem() diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index fdbdf4e0..ac5a30c0 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -5411,6 +5411,15 @@ func TestFillDefaultUnmarshal(t *testing.T) { assert.Equal(t, "c", st.C) }) + t.Run("optional !", func(t *testing.T) { + var st struct { + A string `json:",optional"` + B string `json:",optional=!A"` + } + err := fillDefaultUnmarshal.Unmarshal(map[string]any{}, &st) + assert.NoError(t, err) + }) + t.Run("has value", func(t *testing.T) { type St struct { A string `json:",default=a"` @@ -5857,7 +5866,7 @@ type mockValuerWithParent struct { ok bool } -func (m mockValuerWithParent) Value(key string) (any, bool) { +func (m mockValuerWithParent) Value(_ string) (any, bool) { return m.value, m.ok } diff --git a/core/mapping/utils.go b/core/mapping/utils.go index d7c84af4..f84167e3 100644 --- a/core/mapping/utils.go +++ b/core/mapping/utils.go @@ -416,7 +416,7 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error { } // parseOptions parses the given options in tag. -// for example: `json:"name,options=foo|bar"` or `json:"name,options=[foo,bar]"` +// for example, `json:"name,options=foo|bar"` or `json:"name,options=[foo,bar]"` func parseOptions(val string) []string { if len(val) == 0 { return nil diff --git a/core/mapping/valuer.go b/core/mapping/valuer.go index 206b1b13..24f6f7f3 100644 --- a/core/mapping/valuer.go +++ b/core/mapping/valuer.go @@ -26,9 +26,9 @@ type ( parent valuerWithParent } - // mapValuer is a type for map to meet the Valuer interface. + // mapValuer is a type for the map to meet the Valuer interface. mapValuer map[string]any - // simpleValuer is a type to get value from current node. + // simpleValuer is a type to get value from the current node. simpleValuer node // recursiveValuer is a type to get the value recursively from current and parent nodes. recursiveValuer node diff --git a/core/mr/mapreduce_fuzz_test.go b/core/mr/mapreduce_fuzz_test.go index 936d3623..a275a3ce 100644 --- a/core/mr/mapreduce_fuzz_test.go +++ b/core/mr/mapreduce_fuzz_test.go @@ -13,7 +13,7 @@ import ( ) func FuzzMapReduce(f *testing.F) { - rand.Seed(time.Now().UnixNano()) + rand.NewSource(time.Now().UnixNano()) f.Add(int64(10), runtime.NumCPU()) f.Fuzz(func(t *testing.T, n int64, workers int) { diff --git a/core/mr/mapreduce_fuzzcase_test.go b/core/mr/mapreduce_fuzzcase_test.go index 3b30c31b..ffa3515e 100644 --- a/core/mr/mapreduce_fuzzcase_test.go +++ b/core/mr/mapreduce_fuzzcase_test.go @@ -20,7 +20,7 @@ import ( // If Fuzz stuck, we don't know why, because it only returns hung or unexpected, // so we need to simulate the fuzz test in test mode. func TestMapReduceRandom(t *testing.T) { - rand.Seed(time.Now().UnixNano()) + rand.NewSource(time.Now().UnixNano()) const ( times = 10000 diff --git a/core/proc/goroutines_test.go b/core/proc/goroutines_test.go index 827d0233..4d515462 100644 --- a/core/proc/goroutines_test.go +++ b/core/proc/goroutines_test.go @@ -36,6 +36,6 @@ type fakeCreator struct { err error } -func (fc fakeCreator) Create(name string) (file *os.File, err error) { +func (fc fakeCreator) Create(_ string) (file *os.File, err error) { return fc.file, fc.err } diff --git a/core/queue/queue.go b/core/queue/queue.go index 81aaaae4..5a9fd5c9 100644 --- a/core/queue/queue.go +++ b/core/queue/queue.go @@ -76,7 +76,7 @@ func (q *Queue) AddListener(listener Listener) { q.listeners = append(q.listeners, listener) } -// Broadcast broadcasts message to all event channels. +// Broadcast broadcasts the message to all event channels. func (q *Queue) Broadcast(message any) { go func() { q.eventLock.Lock() @@ -202,7 +202,7 @@ func (q *Queue) produce() { } func (q *Queue) produceOne(producer Producer) (string, bool) { - // avoid panic quit the producer, just log it and continue + // avoid panic quit the producer, log it and continue defer rescue.Recover() return producer.Produce() diff --git a/core/queue/util_test.go b/core/queue/util_test.go index f578c364..eca6b797 100644 --- a/core/queue/util_test.go +++ b/core/queue/util_test.go @@ -67,7 +67,7 @@ func (p *mockedPusher) Name() string { return p.name } -func (p *mockedPusher) Push(s string) error { +func (p *mockedPusher) Push(_ string) error { if proba.TrueOnProba(failProba) { return errors.New("dummy") } diff --git a/core/stat/internal/cpu_linux.go b/core/stat/internal/cpu_linux.go index 245d0ad8..c10196c1 100644 --- a/core/stat/internal/cpu_linux.go +++ b/core/stat/internal/cpu_linux.go @@ -23,42 +23,17 @@ var ( preTotal uint64 limit float64 cores uint64 + noCgroup bool initOnce sync.Once ) -// if /proc not present, ignore the cpu calculation, like wsl linux -func initialize() { - cpus, err := effectiveCpus() - if err != nil { - logx.Error(err) - return - } - - cores = uint64(cpus) - limit = float64(cpus) - quota, err := cpuQuota() - if err == nil && quota > 0 { - if quota < limit { - limit = quota - } - } - - preSystem, err = systemCpuUsage() - if err != nil { - logx.Error(err) - return - } - - preTotal, err = cpuUsage() - if err != nil { - logx.Error(err) - return - } -} - // RefreshCpu refreshes cpu usage and returns. func RefreshCpu() uint64 { - initOnce.Do(initialize) + initializeOnce() + + if noCgroup { + return 0 + } total, err := cpuUsage() if err != nil { @@ -112,6 +87,47 @@ func effectiveCpus() (int, error) { return cg.effectiveCpus() } +// if /proc not present, ignore the cpu calculation, like wsl linux +func initialize() error { + cpus, err := effectiveCpus() + if err != nil { + return err + } + + cores = uint64(cpus) + limit = float64(cpus) + quota, err := cpuQuota() + if err == nil && quota > 0 { + if quota < limit { + limit = quota + } + } + + preSystem, err = systemCpuUsage() + if err != nil { + return err + } + + preTotal, err = cpuUsage() + return err +} + +func initializeOnce() { + initOnce.Do(func() { + defer func() { + if p := recover(); p != nil { + noCgroup = true + logx.Error(p) + } + }() + + if err := initialize(); err != nil { + noCgroup = true + logx.Error(err) + } + }) +} + func systemCpuUsage() (uint64, error) { lines, err := iox.ReadTextLines(statFile, iox.WithoutBlank()) if err != nil { diff --git a/core/stat/metrics_test.go b/core/stat/metrics_test.go index 7172994f..ea686560 100644 --- a/core/stat/metrics_test.go +++ b/core/stat/metrics_test.go @@ -1,11 +1,13 @@ package stat import ( + "errors" "strconv" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/logx/logtest" ) func TestMetrics(t *testing.T) { @@ -30,6 +32,34 @@ func TestMetrics(t *testing.T) { } } +func TestTopDurationWithEmpty(t *testing.T) { + assert.Equal(t, float32(0), getTopDuration(nil)) + assert.Equal(t, float32(0), getTopDuration([]Task{})) +} + +func TestLogAndReport(t *testing.T) { + buf := logtest.NewCollector(t) + old := logEnabled.True() + logEnabled.Set(true) + t.Cleanup(func() { + logEnabled.Set(old) + }) + + log(&StatReport{}) + assert.NotEmpty(t, buf.String()) + + writerLock.Lock() + writer := reportWriter + writerLock.Unlock() + buf = logtest.NewCollector(t) + t.Cleanup(func() { + SetReportWriter(writer) + }) + SetReportWriter(&badWriter{}) + writeReport(&StatReport{}) + assert.NotEmpty(t, buf.String()) +} + type mockedWriter struct { report *StatReport } @@ -38,3 +68,9 @@ func (m *mockedWriter) Write(report *StatReport) error { m.report = report return nil } + +type badWriter struct{} + +func (b *badWriter) Write(_ *StatReport) error { + return errors.New("bad") +} diff --git a/core/stat/task.go b/core/stat/task.go index 637e0b76..203248f2 100644 --- a/core/stat/task.go +++ b/core/stat/task.go @@ -2,7 +2,7 @@ package stat import "time" -// A Task is a task that is reported to Metrics. +// A Task is a task reported to Metrics. type Task struct { Drop bool Duration time.Duration diff --git a/core/stores/cache/cacheopt.go b/core/stores/cache/cacheopt.go index cc8d459d..836c12af 100644 --- a/core/stores/cache/cacheopt.go +++ b/core/stores/cache/cacheopt.go @@ -8,7 +8,7 @@ const ( ) type ( - // An Options is used to store the cache options. + // Options is used to store the cache options. Options struct { Expiry time.Duration NotFoundExpiry time.Duration diff --git a/core/stores/mon/options.go b/core/stores/mon/options.go index 4097328f..cebae6f7 100644 --- a/core/stores/mon/options.go +++ b/core/stores/mon/options.go @@ -1,9 +1,12 @@ package mon import ( + "reflect" "time" "github.com/zeromicro/go-zero/core/syncx" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsoncodec" mopt "go.mongodb.org/mongo-driver/mongo/options" ) @@ -16,10 +19,17 @@ var ( ) type ( - options = mopt.ClientOptions - // Option defines the method to customize a mongo model. Option func(opts *options) + + // TypeCodec is a struct that stores specific type Encoder/Decoder. + TypeCodec struct { + ValueType reflect.Type + Encoder bsoncodec.ValueEncoder + Decoder bsoncodec.ValueDecoder + } + + options = mopt.ClientOptions ) // DisableLog disables logging of mongo commands, includes info and slow logs. @@ -38,15 +48,27 @@ func SetSlowThreshold(threshold time.Duration) { slowThreshold.Set(threshold) } -func defaultTimeoutOption() Option { - return func(opts *options) { - opts.SetTimeout(defaultTimeout) - } -} - // WithTimeout set the mon client operation timeout. func WithTimeout(timeout time.Duration) Option { return func(opts *options) { opts.SetTimeout(timeout) } } + +// WithTypeCodec registers TypeCodecs to convert custom types. +func WithTypeCodec(typeCodecs ...TypeCodec) Option { + return func(opts *options) { + registry := bson.NewRegistry() + for _, v := range typeCodecs { + registry.RegisterTypeEncoder(v.ValueType, v.Encoder) + registry.RegisterTypeDecoder(v.ValueType, v.Decoder) + } + opts.SetRegistry(registry) + } +} + +func defaultTimeoutOption() Option { + return func(opts *options) { + opts.SetTimeout(defaultTimeout) + } +} diff --git a/core/stores/mon/options_test.go b/core/stores/mon/options_test.go index ee944f94..48024dbd 100644 --- a/core/stores/mon/options_test.go +++ b/core/stores/mon/options_test.go @@ -1,10 +1,14 @@ package mon import ( + "fmt" + "reflect" "testing" "time" "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/bsoncodec" + "go.mongodb.org/mongo-driver/bson/bsonrw" mopt "go.mongodb.org/mongo-driver/mongo/options" ) @@ -51,3 +55,56 @@ func TestDisableInfoLog(t *testing.T) { assert.False(t, logMon.True()) assert.True(t, logSlowMon.True()) } + +func TestWithRegistryForTimestampRegisterType(t *testing.T) { + opts := mopt.Client() + + // mongoDateTimeEncoder allow user convert time.Time to primitive.DateTime. + var mongoDateTimeEncoder bsoncodec.ValueEncoderFunc = func(ect bsoncodec.EncodeContext, w bsonrw.ValueWriter, value reflect.Value) error { + // Use reflect, determine if it can be converted to time.Time. + dec, ok := value.Interface().(time.Time) + if !ok { + return fmt.Errorf("value %v to encode is not of type time.Time", value) + } + return w.WriteDateTime(dec.Unix()) + } + + // mongoDateTimeEncoder allow user convert primitive.DateTime to time.Time. + var mongoDateTimeDecoder bsoncodec.ValueDecoderFunc = func(ect bsoncodec.DecodeContext, r bsonrw.ValueReader, value reflect.Value) error { + primTime, err := r.ReadDateTime() + if err != nil { + return fmt.Errorf("error reading primitive.DateTime from ValueReader: %v", err) + } + value.Set(reflect.ValueOf(time.Unix(primTime, 0))) + return nil + } + + codecs := []TypeCodec{ + { + ValueType: reflect.TypeOf(time.Time{}), + Encoder: mongoDateTimeEncoder, + Decoder: mongoDateTimeDecoder, + }, + } + WithTypeCodec(codecs...)(opts) + + for _, v := range codecs { + // Validate Encoder + enc, err := opts.Registry.LookupEncoder(v.ValueType) + if err != nil { + t.Fatal(err) + } + if assert.ObjectsAreEqual(v.Encoder, enc) { + t.Errorf("Encoder got from Registry: %v, but want: %v", enc, v.Encoder) + } + + // Validate Decoder + dec, err := opts.Registry.LookupDecoder(v.ValueType) + if err != nil { + t.Fatal(err) + } + if assert.ObjectsAreEqual(v.Decoder, dec) { + t.Errorf("Decoder got from Registry: %v, but want: %v", dec, v.Decoder) + } + } +} diff --git a/core/stores/redis/breakerhook.go b/core/stores/redis/breakerhook.go new file mode 100644 index 00000000..4d0a1bc0 --- /dev/null +++ b/core/stores/redis/breakerhook.go @@ -0,0 +1,41 @@ +package redis + +import ( + "context" + + red "github.com/redis/go-redis/v9" + "github.com/zeromicro/go-zero/core/breaker" + "github.com/zeromicro/go-zero/core/lang" +) + +var ignoreCmds = map[string]lang.PlaceholderType{ + "blpop": {}, +} + +type breakerHook struct { + brk breaker.Breaker +} + +func (h breakerHook) DialHook(next red.DialHook) red.DialHook { + return next +} + +func (h breakerHook) ProcessHook(next red.ProcessHook) red.ProcessHook { + return func(ctx context.Context, cmd red.Cmder) error { + if _, ok := ignoreCmds[cmd.Name()]; ok { + return next(ctx, cmd) + } + + return h.brk.DoWithAcceptable(func() error { + return next(ctx, cmd) + }, acceptable) + } +} + +func (h breakerHook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook { + return func(ctx context.Context, cmds []red.Cmder) error { + return h.brk.DoWithAcceptable(func() error { + return next(ctx, cmds) + }, acceptable) + } +} diff --git a/core/stores/redis/breakerhook_test.go b/core/stores/redis/breakerhook_test.go new file mode 100644 index 00000000..ebd41504 --- /dev/null +++ b/core/stores/redis/breakerhook_test.go @@ -0,0 +1,135 @@ +package redis + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/breaker" +) + +func TestBreakerHook_ProcessHook(t *testing.T) { + t.Run("breakerHookOpen", func(t *testing.T) { + s := miniredis.RunT(t) + + rds := MustNewRedis(RedisConf{ + Host: s.Addr(), + Type: NodeType, + }) + + someError := errors.New("ERR some error") + s.SetError(someError.Error()) + + var err error + for i := 0; i < 1000; i++ { + _, err = rds.Get("key") + if err != nil && err.Error() != someError.Error() { + break + } + } + assert.Equal(t, breaker.ErrServiceUnavailable, err) + }) + + t.Run("breakerHookClose", func(t *testing.T) { + s := miniredis.RunT(t) + + rds := MustNewRedis(RedisConf{ + Host: s.Addr(), + Type: NodeType, + }) + + var err error + for i := 0; i < 1000; i++ { + _, err = rds.Get("key") + if err != nil { + break + } + } + assert.NotEqual(t, breaker.ErrServiceUnavailable, err) + }) + + t.Run("breakerHook_ignoreCmd", func(t *testing.T) { + s := miniredis.RunT(t) + + rds := MustNewRedis(RedisConf{ + Host: s.Addr(), + Type: NodeType, + }) + + someError := errors.New("ERR some error") + s.SetError(someError.Error()) + + var err error + + node, err := getRedis(rds) + assert.NoError(t, err) + + for i := 0; i < 1000; i++ { + _, err = rds.Blpop(node, "key") + if err != nil && err.Error() != someError.Error() { + break + } + } + assert.Equal(t, someError.Error(), err.Error()) + }) +} + +func TestBreakerHook_ProcessPipelineHook(t *testing.T) { + t.Run("breakerPipelineHookOpen", func(t *testing.T) { + s := miniredis.RunT(t) + + rds := MustNewRedis(RedisConf{ + Host: s.Addr(), + Type: NodeType, + }) + + someError := errors.New("ERR some error") + s.SetError(someError.Error()) + + var err error + for i := 0; i < 1000; i++ { + err = rds.Pipelined( + func(pipe Pipeliner) error { + pipe.Incr(context.Background(), "pipelined_counter") + pipe.Expire(context.Background(), "pipelined_counter", time.Hour) + pipe.ZAdd(context.Background(), "zadd", Z{Score: 12, Member: "zadd"}) + return nil + }, + ) + + if err != nil && err.Error() != someError.Error() { + break + } + } + assert.Equal(t, breaker.ErrServiceUnavailable, err) + }) + + t.Run("breakerPipelineHookClose", func(t *testing.T) { + s := miniredis.RunT(t) + + rds := MustNewRedis(RedisConf{ + Host: s.Addr(), + Type: NodeType, + }) + + var err error + for i := 0; i < 1000; i++ { + err = rds.Pipelined( + func(pipe Pipeliner) error { + pipe.Incr(context.Background(), "pipelined_counter") + pipe.Expire(context.Background(), "pipelined_counter", time.Hour) + pipe.ZAdd(context.Background(), "zadd", Z{Score: 12, Member: "zadd"}) + return nil + }, + ) + + if err != nil { + break + } + } + assert.NotEqual(t, breaker.ErrServiceUnavailable, err) + }) +} diff --git a/core/stores/redis/conf.go b/core/stores/redis/conf.go index c5421087..5328757f 100644 --- a/core/stores/redis/conf.go +++ b/core/stores/redis/conf.go @@ -47,7 +47,7 @@ func (rc RedisConf) NewRedis() *Redis { opts = append(opts, WithTLS()) } - return New(rc.Host, opts...) + return newRedis(rc.Host, opts...) } // Validate validates the RedisConf. diff --git a/core/stores/redis/hook.go b/core/stores/redis/durationhook.go similarity index 87% rename from core/stores/redis/hook.go rename to core/stores/redis/durationhook.go index fca21498..d453360a 100644 --- a/core/stores/redis/hook.go +++ b/core/stores/redis/durationhook.go @@ -23,17 +23,18 @@ import ( const spanName = "redis" var ( - durationHook = hook{} + defaultDurationHook = durationHook{} redisCmdsAttributeKey = attribute.Key("redis.cmds") ) -type hook struct{} +type durationHook struct { +} -func (h hook) DialHook(next red.DialHook) red.DialHook { +func (h durationHook) DialHook(next red.DialHook) red.DialHook { return next } -func (h hook) ProcessHook(next red.ProcessHook) red.ProcessHook { +func (h durationHook) ProcessHook(next red.ProcessHook) red.ProcessHook { return func(ctx context.Context, cmd red.Cmder) error { start := timex.Now() ctx, endSpan := h.startSpan(ctx, cmd) @@ -57,7 +58,7 @@ func (h hook) ProcessHook(next red.ProcessHook) red.ProcessHook { } } -func (h hook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook { +func (h durationHook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipelineHook { return func(ctx context.Context, cmds []red.Cmder) error { if len(cmds) == 0 { return next(ctx, cmds) @@ -83,6 +84,33 @@ func (h hook) ProcessPipelineHook(next red.ProcessPipelineHook) red.ProcessPipel } } +func (h durationHook) startSpan(ctx context.Context, cmds ...red.Cmder) (context.Context, func(err error)) { + tracer := trace.TracerFromContext(ctx) + + ctx, span := tracer.Start(ctx, + spanName, + oteltrace.WithSpanKind(oteltrace.SpanKindClient), + ) + + cmdStrs := make([]string, 0, len(cmds)) + for _, cmd := range cmds { + cmdStrs = append(cmdStrs, cmd.Name()) + } + span.SetAttributes(redisCmdsAttributeKey.StringSlice(cmdStrs)) + + return ctx, func(err error) { + defer span.End() + + if err == nil || errors.Is(err, red.Nil) { + span.SetStatus(codes.Ok, "") + return + } + + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err) + } +} + func formatError(err error) string { if err == nil || errors.Is(err, red.Nil) { return "" @@ -95,7 +123,7 @@ func formatError(err error) string { } switch { - case err == io.EOF: + case errors.Is(err, io.EOF): return "eof" case errors.Is(err, context.DeadlineExceeded): return "context deadline" @@ -123,30 +151,3 @@ func logDuration(ctx context.Context, cmds []red.Cmder, duration time.Duration) } logx.WithContext(ctx).WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String()) } - -func (h hook) startSpan(ctx context.Context, cmds ...red.Cmder) (context.Context, func(err error)) { - tracer := trace.TracerFromContext(ctx) - - ctx, span := tracer.Start(ctx, - spanName, - oteltrace.WithSpanKind(oteltrace.SpanKindClient), - ) - - cmdStrs := make([]string, 0, len(cmds)) - for _, cmd := range cmds { - cmdStrs = append(cmdStrs, cmd.Name()) - } - span.SetAttributes(redisCmdsAttributeKey.StringSlice(cmdStrs)) - - return ctx, func(err error) { - defer span.End() - - if err == nil || errors.Is(err, red.Nil) { - span.SetStatus(codes.Ok, "") - return - } - - span.SetStatus(codes.Error, err.Error()) - span.RecordError(err) - } -} diff --git a/core/stores/redis/hook_test.go b/core/stores/redis/durationhook_test.go similarity index 87% rename from core/stores/redis/hook_test.go rename to core/stores/redis/durationhook_test.go index 44b2e8fa..37c6eb54 100644 --- a/core/stores/redis/hook_test.go +++ b/core/stores/redis/durationhook_test.go @@ -21,7 +21,7 @@ func TestHookProcessCase1(t *testing.T) { tracetest.NewInMemoryExporter(t) w := logtest.NewCollector(t) - err := durationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error { + err := defaultDurationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error { assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name()) return nil })(context.Background(), red.NewCmd(context.Background())) @@ -36,7 +36,7 @@ func TestHookProcessCase2(t *testing.T) { tracetest.NewInMemoryExporter(t) w := logtest.NewCollector(t) - err := durationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error { + err := defaultDurationHook.ProcessHook(func(ctx context.Context, cmd red.Cmder) error { assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name()) time.Sleep(slowThreshold.Load() + time.Millisecond) return nil @@ -54,12 +54,12 @@ func TestHookProcessPipelineCase1(t *testing.T) { tracetest.NewInMemoryExporter(t) w := logtest.NewCollector(t) - err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { + err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { return nil })(context.Background(), nil) assert.NoError(t, err) - err = durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { + err = defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name()) return nil })(context.Background(), []red.Cmder{ @@ -74,7 +74,7 @@ func TestHookProcessPipelineCase2(t *testing.T) { tracetest.NewInMemoryExporter(t) w := logtest.NewCollector(t) - err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { + err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name()) time.Sleep(slowThreshold.Load() + time.Millisecond) return nil @@ -91,7 +91,7 @@ func TestHookProcessPipelineCase2(t *testing.T) { func TestHookProcessPipelineCase3(t *testing.T) { te := tracetest.NewInMemoryExporter(t) - err := durationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { + err := defaultDurationHook.ProcessPipelineHook(func(ctx context.Context, cmds []red.Cmder) error { assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name()) return assert.AnError })(context.Background(), []red.Cmder{ diff --git a/core/stores/redis/redis.go b/core/stores/redis/redis.go index 40fb008c..adce614a 100644 --- a/core/stores/redis/redis.go +++ b/core/stores/redis/redis.go @@ -156,21 +156,16 @@ func (s *Redis) BitCount(key string, start, end int64) (int64, error) { } // BitCountCtx is redis bitcount command implementation. -func (s *Redis) BitCountCtx(ctx context.Context, key string, start, end int64) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitCountCtx(ctx context.Context, key string, start, end int64) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitCount(ctx, key, &red.BitCount{ - Start: start, - End: end, - }).Result() - return err - }, acceptable) - - return + return conn.BitCount(ctx, key, &red.BitCount{ + Start: start, + End: end, + }).Result() } // BitOpAnd is redis bit operation (and) command implementation. @@ -179,18 +174,13 @@ func (s *Redis) BitOpAnd(destKey string, keys ...string) (int64, error) { } // BitOpAndCtx is redis bit operation (and) command implementation. -func (s *Redis) BitOpAndCtx(ctx context.Context, destKey string, keys ...string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitOpAndCtx(ctx context.Context, destKey string, keys ...string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitOpAnd(ctx, destKey, keys...).Result() - return err - }, acceptable) - - return + return conn.BitOpAnd(ctx, destKey, keys...).Result() } // BitOpNot is redis bit operation (not) command implementation. @@ -199,18 +189,13 @@ func (s *Redis) BitOpNot(destKey, key string) (int64, error) { } // BitOpNotCtx is redis bit operation (not) command implementation. -func (s *Redis) BitOpNotCtx(ctx context.Context, destKey, key string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitOpNotCtx(ctx context.Context, destKey, key string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitOpNot(ctx, destKey, key).Result() - return err - }, acceptable) - - return + return conn.BitOpNot(ctx, destKey, key).Result() } // BitOpOr is redis bit operation (or) command implementation. @@ -219,18 +204,13 @@ func (s *Redis) BitOpOr(destKey string, keys ...string) (int64, error) { } // BitOpOrCtx is redis bit operation (or) command implementation. -func (s *Redis) BitOpOrCtx(ctx context.Context, destKey string, keys ...string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitOpOrCtx(ctx context.Context, destKey string, keys ...string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitOpOr(ctx, destKey, keys...).Result() - return err - }, acceptable) - - return + return conn.BitOpOr(ctx, destKey, keys...).Result() } // BitOpXor is redis bit operation (xor) command implementation. @@ -239,18 +219,13 @@ func (s *Redis) BitOpXor(destKey string, keys ...string) (int64, error) { } // BitOpXorCtx is redis bit operation (xor) command implementation. -func (s *Redis) BitOpXorCtx(ctx context.Context, destKey string, keys ...string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitOpXorCtx(ctx context.Context, destKey string, keys ...string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitOpXor(ctx, destKey, keys...).Result() - return err - }, acceptable) - - return + return conn.BitOpXor(ctx, destKey, keys...).Result() } // BitPos is redis bitpos command implementation. @@ -259,18 +234,13 @@ func (s *Redis) BitPos(key string, bit, start, end int64) (int64, error) { } // BitPosCtx is redis bitpos command implementation. -func (s *Redis) BitPosCtx(ctx context.Context, key string, bit, start, end int64) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) BitPosCtx(ctx context.Context, key string, bit, start, end int64) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.BitPos(ctx, key, bit, start, end).Result() - return err - }, acceptable) - - return + return conn.BitPos(ctx, key, bit, start, end).Result() } // Blpop uses passed in redis connection to execute blocking queries. @@ -342,18 +312,13 @@ func (s *Redis) Decr(key string) (int64, error) { } // DecrCtx is the implementation of redis decr command. -func (s *Redis) DecrCtx(ctx context.Context, key string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) DecrCtx(ctx context.Context, key string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.Decr(ctx, key).Result() - return err - }, acceptable) - - return + return conn.Decr(ctx, key).Result() } // Decrby is the implementation of redis decrby command. @@ -362,18 +327,13 @@ func (s *Redis) Decrby(key string, decrement int64) (int64, error) { } // DecrbyCtx is the implementation of redis decrby command. -func (s *Redis) DecrbyCtx(ctx context.Context, key string, decrement int64) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) DecrbyCtx(ctx context.Context, key string, decrement int64) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.DecrBy(ctx, key, decrement).Result() - return err - }, acceptable) - - return + return conn.DecrBy(ctx, key, decrement).Result() } // Del deletes keys. @@ -382,23 +342,18 @@ func (s *Redis) Del(keys ...string) (int, error) { } // DelCtx deletes keys. -func (s *Redis) DelCtx(ctx context.Context, keys ...string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) DelCtx(ctx context.Context, keys ...string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.Del(ctx, keys...).Result() - if err != nil { - return err - } + v, err := conn.Del(ctx, keys...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Eval is the implementation of redis eval command. @@ -408,18 +363,13 @@ func (s *Redis) Eval(script string, keys []string, args ...any) (any, error) { // EvalCtx is the implementation of redis eval command. func (s *Redis) EvalCtx(ctx context.Context, script string, keys []string, - args ...any) (val any, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + args ...any) (any, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.Eval(ctx, script, keys, args...).Result() - return err - }, acceptable) - - return + return conn.Eval(ctx, script, keys, args...).Result() } // EvalSha is the implementation of redis evalsha command. @@ -429,18 +379,13 @@ func (s *Redis) EvalSha(sha string, keys []string, args ...any) (any, error) { // EvalShaCtx is the implementation of redis evalsha command. func (s *Redis) EvalShaCtx(ctx context.Context, sha string, keys []string, - args ...any) (val any, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + args ...any) (any, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.EvalSha(ctx, sha, keys, args...).Result() - return err - }, acceptable) - - return + return conn.EvalSha(ctx, sha, keys, args...).Result() } // Exists is the implementation of redis exists command. @@ -449,23 +394,18 @@ func (s *Redis) Exists(key string) (bool, error) { } // ExistsCtx is the implementation of redis exists command. -func (s *Redis) ExistsCtx(ctx context.Context, key string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ExistsCtx(ctx context.Context, key string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - v, err := conn.Exists(ctx, key).Result() - if err != nil { - return err - } + v, err := conn.Exists(ctx, key).Result() + if err != nil { + return false, err + } - val = v == 1 - return nil - }, acceptable) - - return + return v == 1, nil } // ExistsMany is the implementation of redis exists command. @@ -476,23 +416,13 @@ func (s *Redis) ExistsMany(keys ...string) (int64, error) { // ExistsManyCtx is the implementation of redis exists command. // checks the existence of multiple keys in Redis using the EXISTS command. -func (s *Redis) ExistsManyCtx(ctx context.Context, keys ...string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ExistsManyCtx(ctx context.Context, keys ...string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.Exists(ctx, keys...).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.Exists(ctx, keys...).Result() } // Expire is the implementation of redis expire command. @@ -502,14 +432,12 @@ func (s *Redis) Expire(key string, seconds int) error { // ExpireCtx is the implementation of redis expire command. func (s *Redis) ExpireCtx(ctx context.Context, key string, seconds int) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.Expire(ctx, key, time.Duration(seconds)*time.Second).Err() - }, acceptable) + return conn.Expire(ctx, key, time.Duration(seconds)*time.Second).Err() } // Expireat is the implementation of redis expireat command. @@ -519,14 +447,12 @@ func (s *Redis) Expireat(key string, expireTime int64) error { // ExpireatCtx is the implementation of redis expireat command. func (s *Redis) ExpireatCtx(ctx context.Context, key string, expireTime int64) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.ExpireAt(ctx, key, time.Unix(expireTime, 0)).Err() - }, acceptable) + return conn.ExpireAt(ctx, key, time.Unix(expireTime, 0)).Err() } // GeoAdd is the implementation of redis geoadd command. @@ -536,23 +462,13 @@ func (s *Redis) GeoAdd(key string, geoLocation ...*GeoLocation) (int64, error) { // GeoAddCtx is the implementation of redis geoadd command. func (s *Redis) GeoAddCtx(ctx context.Context, key string, geoLocation ...*GeoLocation) ( - val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.GeoAdd(ctx, key, geoLocation...).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoAdd(ctx, key, geoLocation...).Result() } // GeoDist is the implementation of redis geodist command. @@ -562,23 +478,13 @@ func (s *Redis) GeoDist(key, member1, member2, unit string) (float64, error) { // GeoDistCtx is the implementation of redis geodist command. func (s *Redis) GeoDistCtx(ctx context.Context, key, member1, member2, unit string) ( - val float64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + float64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.GeoDist(ctx, key, member1, member2, unit).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoDist(ctx, key, member1, member2, unit).Result() } // GeoHash is the implementation of redis geohash command. @@ -588,23 +494,13 @@ func (s *Redis) GeoHash(key string, members ...string) ([]string, error) { // GeoHashCtx is the implementation of redis geohash command. func (s *Redis) GeoHashCtx(ctx context.Context, key string, members ...string) ( - val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.GeoHash(ctx, key, members...).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoHash(ctx, key, members...).Result() } // GeoRadius is the implementation of redis georadius command. @@ -615,23 +511,13 @@ func (s *Redis) GeoRadius(key string, longitude, latitude float64, query *GeoRad // GeoRadiusCtx is the implementation of redis georadius command. func (s *Redis) GeoRadiusCtx(ctx context.Context, key string, longitude, latitude float64, - query *GeoRadiusQuery) (val []GeoLocation, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + query *GeoRadiusQuery) ([]GeoLocation, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.GeoRadius(ctx, key, longitude, latitude, query).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoRadius(ctx, key, longitude, latitude, query).Result() } // GeoRadiusByMember is the implementation of redis georadiusbymember command. @@ -641,23 +527,13 @@ func (s *Redis) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) ([] // GeoRadiusByMemberCtx is the implementation of redis georadiusbymember command. func (s *Redis) GeoRadiusByMemberCtx(ctx context.Context, key, member string, - query *GeoRadiusQuery) (val []GeoLocation, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + query *GeoRadiusQuery) ([]GeoLocation, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.GeoRadiusByMember(ctx, key, member, query).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoRadiusByMember(ctx, key, member, query).Result() } // GeoPos is the implementation of redis geopos command. @@ -667,23 +543,13 @@ func (s *Redis) GeoPos(key string, members ...string) ([]*GeoPos, error) { // GeoPosCtx is the implementation of redis geopos command. func (s *Redis) GeoPosCtx(ctx context.Context, key string, members ...string) ( - val []*GeoPos, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []*GeoPos, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.GeoPos(ctx, key, members...).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.GeoPos(ctx, key, members...).Result() } // Get is the implementation of redis get command. @@ -692,23 +558,19 @@ func (s *Redis) Get(key string) (string, error) { } // GetCtx is the implementation of redis get command. -func (s *Redis) GetCtx(ctx context.Context, key string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) GetCtx(ctx context.Context, key string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - if val, err = conn.Get(ctx, key).Result(); err == red.Nil { - return nil - } else if err != nil { - return err - } else { - return nil - } - }, acceptable) - - return + if val, err := conn.Get(ctx, key).Result(); errors.Is(err, red.Nil) { + return "", nil + } else if err != nil { + return "", err + } else { + return val, nil + } } // GetBit is the implementation of redis getbit command. @@ -717,23 +579,18 @@ func (s *Redis) GetBit(key string, offset int64) (int, error) { } // GetBitCtx is the implementation of redis getbit command. -func (s *Redis) GetBitCtx(ctx context.Context, key string, offset int64) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) GetBitCtx(ctx context.Context, key string, offset int64) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.GetBit(ctx, key, offset).Result() - if err != nil { - return err - } + v, err := conn.GetBit(ctx, key, offset).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // GetSet is the implementation of redis getset command. @@ -742,21 +599,18 @@ func (s *Redis) GetSet(key, value string) (string, error) { } // GetSetCtx is the implementation of redis getset command. -func (s *Redis) GetSetCtx(ctx context.Context, key, value string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) GetSetCtx(ctx context.Context, key, value string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - if val, err = conn.GetSet(ctx, key, value).Result(); err == red.Nil { - return nil - } + val, err := conn.GetSet(ctx, key, value).Result() + if errors.Is(err, red.Nil) { + return "", nil + } - return err - }, acceptable) - - return + return val, err } // Hdel is the implementation of redis hdel command. @@ -765,23 +619,18 @@ func (s *Redis) Hdel(key string, fields ...string) (bool, error) { } // HdelCtx is the implementation of redis hdel command. -func (s *Redis) HdelCtx(ctx context.Context, key string, fields ...string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HdelCtx(ctx context.Context, key string, fields ...string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - v, err := conn.HDel(ctx, key, fields...).Result() - if err != nil { - return err - } + v, err := conn.HDel(ctx, key, fields...).Result() + if err != nil { + return false, err + } - val = v >= 1 - return nil - }, acceptable) - - return + return v >= 1, nil } // Hexists is the implementation of redis hexists command. @@ -790,18 +639,13 @@ func (s *Redis) Hexists(key, field string) (bool, error) { } // HexistsCtx is the implementation of redis hexists command. -func (s *Redis) HexistsCtx(ctx context.Context, key, field string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HexistsCtx(ctx context.Context, key, field string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.HExists(ctx, key, field).Result() - return err - }, acceptable) - - return + return conn.HExists(ctx, key, field).Result() } // Hget is the implementation of redis hget command. @@ -810,18 +654,13 @@ func (s *Redis) Hget(key, field string) (string, error) { } // HgetCtx is the implementation of redis hget command. -func (s *Redis) HgetCtx(ctx context.Context, key, field string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HgetCtx(ctx context.Context, key, field string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - val, err = conn.HGet(ctx, key, field).Result() - return err - }, acceptable) - - return + return conn.HGet(ctx, key, field).Result() } // Hgetall is the implementation of redis hgetall command. @@ -830,18 +669,13 @@ func (s *Redis) Hgetall(key string) (map[string]string, error) { } // HgetallCtx is the implementation of redis hgetall command. -func (s *Redis) HgetallCtx(ctx context.Context, key string) (val map[string]string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HgetallCtx(ctx context.Context, key string) (map[string]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.HGetAll(ctx, key).Result() - return err - }, acceptable) - - return + return conn.HGetAll(ctx, key).Result() } // Hincrby is the implementation of redis hincrby command. @@ -850,23 +684,18 @@ func (s *Redis) Hincrby(key, field string, increment int) (int, error) { } // HincrbyCtx is the implementation of redis hincrby command. -func (s *Redis) HincrbyCtx(ctx context.Context, key, field string, increment int) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HincrbyCtx(ctx context.Context, key, field string, increment int) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.HIncrBy(ctx, key, field, int64(increment)).Result() - if err != nil { - return err - } + v, err := conn.HIncrBy(ctx, key, field, int64(increment)).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // HincrbyFloat is the implementation of redis hincrbyfloat command. @@ -875,20 +704,14 @@ func (s *Redis) HincrbyFloat(key, field string, increment float64) (float64, err } // HincrbyFloatCtx is the implementation of redis hincrbyfloat command. -func (s *Redis) HincrbyFloatCtx(ctx context.Context, key, field string, increment float64) (val float64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } - val, err = conn.HIncrByFloat(ctx, key, field, increment).Result() - if err != nil { - return err - } - return nil - }, acceptable) +func (s *Redis) HincrbyFloatCtx(ctx context.Context, key, field string, increment float64) ( + float64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - return + return conn.HIncrByFloat(ctx, key, field, increment).Result() } // Hkeys is the implementation of redis hkeys command. @@ -897,18 +720,13 @@ func (s *Redis) Hkeys(key string) ([]string, error) { } // HkeysCtx is the implementation of redis hkeys command. -func (s *Redis) HkeysCtx(ctx context.Context, key string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HkeysCtx(ctx context.Context, key string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.HKeys(ctx, key).Result() - return err - }, acceptable) - - return + return conn.HKeys(ctx, key).Result() } // Hlen is the implementation of redis hlen command. @@ -917,23 +735,18 @@ func (s *Redis) Hlen(key string) (int, error) { } // HlenCtx is the implementation of redis hlen command. -func (s *Redis) HlenCtx(ctx context.Context, key string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HlenCtx(ctx context.Context, key string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.HLen(ctx, key).Result() - if err != nil { - return err - } + v, err := conn.HLen(ctx, key).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Hmget is the implementation of redis hmget command. @@ -942,23 +755,18 @@ func (s *Redis) Hmget(key string, fields ...string) ([]string, error) { } // HmgetCtx is the implementation of redis hmget command. -func (s *Redis) HmgetCtx(ctx context.Context, key string, fields ...string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HmgetCtx(ctx context.Context, key string, fields ...string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.HMGet(ctx, key, fields...).Result() - if err != nil { - return err - } + v, err := conn.HMGet(ctx, key, fields...).Result() + if err != nil { + return nil, err + } - val = toStrings(v) - return nil - }, acceptable) - - return + return toStrings(v), nil } // Hset is the implementation of redis hset command. @@ -968,14 +776,12 @@ func (s *Redis) Hset(key, field, value string) error { // HsetCtx is the implementation of redis hset command. func (s *Redis) HsetCtx(ctx context.Context, key, field, value string) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.HSet(ctx, key, field, value).Err() - }, acceptable) + return conn.HSet(ctx, key, field, value).Err() } // Hsetnx is the implementation of redis hsetnx command. @@ -984,18 +790,13 @@ func (s *Redis) Hsetnx(key, field, value string) (bool, error) { } // HsetnxCtx is the implementation of redis hsetnx command. -func (s *Redis) HsetnxCtx(ctx context.Context, key, field, value string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HsetnxCtx(ctx context.Context, key, field, value string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.HSetNX(ctx, key, field, value).Result() - return err - }, acceptable) - - return + return conn.HSetNX(ctx, key, field, value).Result() } // Hmset is the implementation of redis hmset command. @@ -1005,41 +806,34 @@ func (s *Redis) Hmset(key string, fieldsAndValues map[string]string) error { // HmsetCtx is the implementation of redis hmset command. func (s *Redis) HmsetCtx(ctx context.Context, key string, fieldsAndValues map[string]string) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - vals := make(map[string]any, len(fieldsAndValues)) - for k, v := range fieldsAndValues { - vals[k] = v - } + vals := make(map[string]any, len(fieldsAndValues)) + for k, v := range fieldsAndValues { + vals[k] = v + } - return conn.HMSet(ctx, key, vals).Err() - }, acceptable) + return conn.HMSet(ctx, key, vals).Err() } // Hscan is the implementation of redis hscan command. func (s *Redis) Hscan(key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { + []string, uint64, error) { return s.HscanCtx(context.Background(), key, cursor, match, count) } // HscanCtx is the implementation of redis hscan command. func (s *Redis) HscanCtx(ctx context.Context, key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, uint64, error) { + conn, err := getRedis(s) + if err != nil { + return nil, 0, err + } - keys, cur, err = conn.HScan(ctx, key, cursor, match, count).Result() - return err - }, acceptable) - - return + return conn.HScan(ctx, key, cursor, match, count).Result() } // Hvals is the implementation of redis hvals command. @@ -1048,18 +842,13 @@ func (s *Redis) Hvals(key string) ([]string, error) { } // HvalsCtx is the implementation of redis hvals command. -func (s *Redis) HvalsCtx(ctx context.Context, key string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) HvalsCtx(ctx context.Context, key string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.HVals(ctx, key).Result() - return err - }, acceptable) - - return + return conn.HVals(ctx, key).Result() } // Incr is the implementation of redis incr command. @@ -1068,18 +857,13 @@ func (s *Redis) Incr(key string) (int64, error) { } // IncrCtx is the implementation of redis incr command. -func (s *Redis) IncrCtx(ctx context.Context, key string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) IncrCtx(ctx context.Context, key string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.Incr(ctx, key).Result() - return err - }, acceptable) - - return + return conn.Incr(ctx, key).Result() } // Incrby is the implementation of redis incrby command. @@ -1088,18 +872,13 @@ func (s *Redis) Incrby(key string, increment int64) (int64, error) { } // IncrbyCtx is the implementation of redis incrby command. -func (s *Redis) IncrbyCtx(ctx context.Context, key string, increment int64) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) IncrbyCtx(ctx context.Context, key string, increment int64) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.IncrBy(ctx, key, increment).Result() - return err - }, acceptable) - - return + return conn.IncrBy(ctx, key, increment).Result() } // IncrbyFloat is the implementation of redis hincrbyfloat command. @@ -1108,18 +887,13 @@ func (s *Redis) IncrbyFloat(key string, increment float64) (float64, error) { } // IncrbyFloatCtx is the implementation of redis hincrbyfloat command. -func (s *Redis) IncrbyFloatCtx(ctx context.Context, key string, increment float64) (val float64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) IncrbyFloatCtx(ctx context.Context, key string, increment float64) (float64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.IncrByFloat(ctx, key, increment).Result() - return err - }, acceptable) - - return + return conn.IncrByFloat(ctx, key, increment).Result() } // Keys is the implementation of redis keys command. @@ -1128,18 +902,13 @@ func (s *Redis) Keys(pattern string) ([]string, error) { } // KeysCtx is the implementation of redis keys command. -func (s *Redis) KeysCtx(ctx context.Context, pattern string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) KeysCtx(ctx context.Context, pattern string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.Keys(ctx, pattern).Result() - return err - }, acceptable) - - return + return conn.Keys(ctx, pattern).Result() } // Llen is the implementation of redis llen command. @@ -1148,23 +917,18 @@ func (s *Redis) Llen(key string) (int, error) { } // LlenCtx is the implementation of redis llen command. -func (s *Redis) LlenCtx(ctx context.Context, key string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LlenCtx(ctx context.Context, key string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.LLen(ctx, key).Result() - if err != nil { - return err - } + v, err := conn.LLen(ctx, key).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Lindex is the implementation of redis lindex command. @@ -1173,18 +937,13 @@ func (s *Redis) Lindex(key string, index int64) (string, error) { } // LindexCtx is the implementation of redis lindex command. -func (s *Redis) LindexCtx(ctx context.Context, key string, index int64) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LindexCtx(ctx context.Context, key string, index int64) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - val, err = conn.LIndex(ctx, key, index).Result() - return err - }, acceptable) - - return + return conn.LIndex(ctx, key, index).Result() } // Lpop is the implementation of redis lpop command. @@ -1193,18 +952,13 @@ func (s *Redis) Lpop(key string) (string, error) { } // LpopCtx is the implementation of redis lpop command. -func (s *Redis) LpopCtx(ctx context.Context, key string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LpopCtx(ctx context.Context, key string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - val, err = conn.LPop(ctx, key).Result() - return err - }, acceptable) - - return + return conn.LPop(ctx, key).Result() } // LpopCount is the implementation of redis lpopCount command. @@ -1213,18 +967,13 @@ func (s *Redis) LpopCount(key string, count int) ([]string, error) { } // LpopCountCtx is the implementation of redis lpopCount command. -func (s *Redis) LpopCountCtx(ctx context.Context, key string, count int) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LpopCountCtx(ctx context.Context, key string, count int) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.LPopCount(ctx, key, count).Result() - return err - }, acceptable) - - return + return conn.LPopCount(ctx, key, count).Result() } // Lpush is the implementation of redis lpush command. @@ -1233,23 +982,18 @@ func (s *Redis) Lpush(key string, values ...any) (int, error) { } // LpushCtx is the implementation of redis lpush command. -func (s *Redis) LpushCtx(ctx context.Context, key string, values ...any) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LpushCtx(ctx context.Context, key string, values ...any) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.LPush(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.LPush(ctx, key, values...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Lrange is the implementation of redis lrange command. @@ -1258,18 +1002,13 @@ func (s *Redis) Lrange(key string, start, stop int) ([]string, error) { } // LrangeCtx is the implementation of redis lrange command. -func (s *Redis) LrangeCtx(ctx context.Context, key string, start, stop int) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LrangeCtx(ctx context.Context, key string, start, stop int) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.LRange(ctx, key, int64(start), int64(stop)).Result() - return err - }, acceptable) - - return + return conn.LRange(ctx, key, int64(start), int64(stop)).Result() } // Lrem is the implementation of redis lrem command. @@ -1278,23 +1017,18 @@ func (s *Redis) Lrem(key string, count int, value string) (int, error) { } // LremCtx is the implementation of redis lrem command. -func (s *Redis) LremCtx(ctx context.Context, key string, count int, value string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) LremCtx(ctx context.Context, key string, count int, value string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.LRem(ctx, key, int64(count), value).Result() - if err != nil { - return err - } + v, err := conn.LRem(ctx, key, int64(count), value).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Ltrim is the implementation of redis ltrim command. @@ -1304,14 +1038,12 @@ func (s *Redis) Ltrim(key string, start, stop int64) error { // LtrimCtx is the implementation of redis ltrim command. func (s *Redis) LtrimCtx(ctx context.Context, key string, start, stop int64) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.LTrim(ctx, key, start, stop).Err() - }, acceptable) + return conn.LTrim(ctx, key, start, stop).Err() } // Mget is the implementation of redis mget command. @@ -1320,23 +1052,33 @@ func (s *Redis) Mget(keys ...string) ([]string, error) { } // MgetCtx is the implementation of redis mget command. -func (s *Redis) MgetCtx(ctx context.Context, keys ...string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) MgetCtx(ctx context.Context, keys ...string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.MGet(ctx, keys...).Result() - if err != nil { - return err - } + v, err := conn.MGet(ctx, keys...).Result() + if err != nil { + return nil, err + } - val = toStrings(v) - return nil - }, acceptable) + return toStrings(v), nil +} - return +// Mset is the implementation of redis mset command. +func (s *Redis) Mset(fieldsAndValues ...any) (string, error) { + return s.MsetCtx(context.Background(), fieldsAndValues...) +} + +// MsetCtx is the implementation of redis mset command. +func (s *Redis) MsetCtx(ctx context.Context, fieldsAndValues ...any) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } + + return conn.MSet(ctx, fieldsAndValues...).Result() } // Persist is the implementation of redis persist command. @@ -1345,18 +1087,13 @@ func (s *Redis) Persist(key string) (bool, error) { } // PersistCtx is the implementation of redis persist command. -func (s *Redis) PersistCtx(ctx context.Context, key string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) PersistCtx(ctx context.Context, key string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.Persist(ctx, key).Result() - return err - }, acceptable) - - return + return conn.Persist(ctx, key).Result() } // Pfadd is the implementation of redis pfadd command. @@ -1365,23 +1102,18 @@ func (s *Redis) Pfadd(key string, values ...any) (bool, error) { } // PfaddCtx is the implementation of redis pfadd command. -func (s *Redis) PfaddCtx(ctx context.Context, key string, values ...any) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) PfaddCtx(ctx context.Context, key string, values ...any) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - v, err := conn.PFAdd(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.PFAdd(ctx, key, values...).Result() + if err != nil { + return false, err + } - val = v >= 1 - return nil - }, acceptable) - - return + return v >= 1, nil } // Pfcount is the implementation of redis pfcount command. @@ -1390,18 +1122,13 @@ func (s *Redis) Pfcount(key string) (int64, error) { } // PfcountCtx is the implementation of redis pfcount command. -func (s *Redis) PfcountCtx(ctx context.Context, key string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) PfcountCtx(ctx context.Context, key string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.PFCount(ctx, key).Result() - return err - }, acceptable) - - return + return conn.PFCount(ctx, key).Result() } // Pfmerge is the implementation of redis pfmerge command. @@ -1411,15 +1138,13 @@ func (s *Redis) Pfmerge(dest string, keys ...string) error { // PfmergeCtx is the implementation of redis pfmerge command. func (s *Redis) PfmergeCtx(ctx context.Context, dest string, keys ...string) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } - - _, err = conn.PFMerge(ctx, dest, keys...).Result() + conn, err := getRedis(s) + if err != nil { return err - }, acceptable) + } + + _, err = conn.PFMerge(ctx, dest, keys...).Result() + return err } // Ping is the implementation of redis ping command. @@ -1428,26 +1153,19 @@ func (s *Redis) Ping() bool { } // PingCtx is the implementation of redis ping command. -func (s *Redis) PingCtx(ctx context.Context) (val bool) { +func (s *Redis) PingCtx(ctx context.Context) bool { // ignore error, error means false - _ = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - val = false - return nil - } + conn, err := getRedis(s) + if err != nil { + return false + } - v, err := conn.Ping(ctx).Result() - if err != nil { - val = false - return nil - } + v, err := conn.Ping(ctx).Result() + if err != nil { + return false + } - val = v == "PONG" - return nil - }, acceptable) - - return + return v == "PONG" } // Pipelined lets fn execute pipelined commands. @@ -1458,15 +1176,13 @@ func (s *Redis) Pipelined(fn func(Pipeliner) error) error { // PipelinedCtx lets fn execute pipelined commands. // Results need to be retrieved by calling Pipeline.Exec() func (s *Redis) PipelinedCtx(ctx context.Context, fn func(Pipeliner) error) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } - - _, err = conn.Pipelined(ctx, fn) + conn, err := getRedis(s) + if err != nil { return err - }, acceptable) + } + + _, err = conn.Pipelined(ctx, fn) + return err } // Rpop is the implementation of redis rpop command. @@ -1475,18 +1191,13 @@ func (s *Redis) Rpop(key string) (string, error) { } // RpopCtx is the implementation of redis rpop command. -func (s *Redis) RpopCtx(ctx context.Context, key string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) RpopCtx(ctx context.Context, key string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - val, err = conn.RPop(ctx, key).Result() - return err - }, acceptable) - - return + return conn.RPop(ctx, key).Result() } // RpopCount is the implementation of redis rpopCount command. @@ -1495,18 +1206,13 @@ func (s *Redis) RpopCount(key string, count int) ([]string, error) { } // RpopCountCtx is the implementation of redis rpopCount command. -func (s *Redis) RpopCountCtx(ctx context.Context, key string, count int) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) RpopCountCtx(ctx context.Context, key string, count int) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.RPopCount(ctx, key, count).Result() - return err - }, acceptable) - - return + return conn.RPopCount(ctx, key, count).Result() } // Rpush is the implementation of redis rpush command. @@ -1515,23 +1221,18 @@ func (s *Redis) Rpush(key string, values ...any) (int, error) { } // RpushCtx is the implementation of redis rpush command. -func (s *Redis) RpushCtx(ctx context.Context, key string, values ...any) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) RpushCtx(ctx context.Context, key string, values ...any) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.RPush(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.RPush(ctx, key, values...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Sadd is the implementation of redis sadd command. @@ -1540,44 +1241,34 @@ func (s *Redis) Sadd(key string, values ...any) (int, error) { } // SaddCtx is the implementation of redis sadd command. -func (s *Redis) SaddCtx(ctx context.Context, key string, values ...any) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SaddCtx(ctx context.Context, key string, values ...any) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SAdd(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.SAdd(ctx, key, values...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Scan is the implementation of redis scan command. -func (s *Redis) Scan(cursor uint64, match string, count int64) (keys []string, cur uint64, err error) { +func (s *Redis) Scan(cursor uint64, match string, count int64) ([]string, uint64, error) { return s.ScanCtx(context.Background(), cursor, match, count) } // ScanCtx is the implementation of redis scan command. func (s *Redis) ScanCtx(ctx context.Context, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, uint64, error) { + conn, err := getRedis(s) + if err != nil { + return nil, 0, err + } - keys, cur, err = conn.Scan(ctx, cursor, match, count).Result() - return err - }, acceptable) - - return + return conn.Scan(ctx, cursor, match, count).Result() } // SetBit is the implementation of redis setbit command. @@ -1586,45 +1277,35 @@ func (s *Redis) SetBit(key string, offset int64, value int) (int, error) { } // SetBitCtx is the implementation of redis setbit command. -func (s *Redis) SetBitCtx(ctx context.Context, key string, offset int64, value int) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SetBitCtx(ctx context.Context, key string, offset int64, value int) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SetBit(ctx, key, offset, value).Result() - if err != nil { - return err - } + v, err := conn.SetBit(ctx, key, offset, value).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Sscan is the implementation of redis sscan command. func (s *Redis) Sscan(key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { + []string, uint64, error) { return s.SscanCtx(context.Background(), key, cursor, match, count) } // SscanCtx is the implementation of redis sscan command. func (s *Redis) SscanCtx(ctx context.Context, key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, uint64, error) { + conn, err := getRedis(s) + if err != nil { + return nil, 0, err + } - keys, cur, err = conn.SScan(ctx, key, cursor, match, count).Result() - return err - }, acceptable) - - return + return conn.SScan(ctx, key, cursor, match, count).Result() } // Scard is the implementation of redis scard command. @@ -1633,18 +1314,13 @@ func (s *Redis) Scard(key string) (int64, error) { } // ScardCtx is the implementation of redis scard command. -func (s *Redis) ScardCtx(ctx context.Context, key string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ScardCtx(ctx context.Context, key string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.SCard(ctx, key).Result() - return err - }, acceptable) - - return + return conn.SCard(ctx, key).Result() } // ScriptLoad is the implementation of redis script load command. @@ -1668,17 +1344,14 @@ func (s *Redis) ScriptRun(script *Script, keys []string, args ...any) (any, erro } // ScriptRunCtx is the implementation of *redis.Script run command. -func (s *Redis) ScriptRunCtx(ctx context.Context, script *Script, keys []string, args ...any) (val any, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ScriptRunCtx(ctx context.Context, script *Script, keys []string, + args ...any) (any, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = script.Run(ctx, conn, keys, args...).Result() - return err - }, acceptable) - return + return script.Run(ctx, conn, keys, args...).Result() } // Set is the implementation of redis set command. @@ -1688,14 +1361,12 @@ func (s *Redis) Set(key, value string) error { // SetCtx is the implementation of redis set command. func (s *Redis) SetCtx(ctx context.Context, key, value string) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.Set(ctx, key, value, 0).Err() - }, acceptable) + return conn.Set(ctx, key, value, 0).Err() } // Setex is the implementation of redis setex command. @@ -1705,14 +1376,12 @@ func (s *Redis) Setex(key, value string, seconds int) error { // SetexCtx is the implementation of redis setex command. func (s *Redis) SetexCtx(ctx context.Context, key, value string, seconds int) error { - return s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return err + } - return conn.Set(ctx, key, value, time.Duration(seconds)*time.Second).Err() - }, acceptable) + return conn.Set(ctx, key, value, time.Duration(seconds)*time.Second).Err() } // Setnx is the implementation of redis setnx command. @@ -1721,18 +1390,13 @@ func (s *Redis) Setnx(key, value string) (bool, error) { } // SetnxCtx is the implementation of redis setnx command. -func (s *Redis) SetnxCtx(ctx context.Context, key, value string) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SetnxCtx(ctx context.Context, key, value string) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.SetNX(ctx, key, value, 0).Result() - return err - }, acceptable) - - return + return conn.SetNX(ctx, key, value, 0).Result() } // SetnxEx is the implementation of redis setnx command with expire. @@ -1741,18 +1405,13 @@ func (s *Redis) SetnxEx(key, value string, seconds int) (bool, error) { } // SetnxExCtx is the implementation of redis setnx command with expire. -func (s *Redis) SetnxExCtx(ctx context.Context, key, value string, seconds int) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SetnxExCtx(ctx context.Context, key, value string, seconds int) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.SetNX(ctx, key, value, time.Duration(seconds)*time.Second).Result() - return err - }, acceptable) - - return + return conn.SetNX(ctx, key, value, time.Duration(seconds)*time.Second).Result() } // Sismember is the implementation of redis sismember command. @@ -1761,18 +1420,13 @@ func (s *Redis) Sismember(key string, value any) (bool, error) { } // SismemberCtx is the implementation of redis sismember command. -func (s *Redis) SismemberCtx(ctx context.Context, key string, value any) (val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SismemberCtx(ctx context.Context, key string, value any) (bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - val, err = conn.SIsMember(ctx, key, value).Result() - return err - }, acceptable) - - return + return conn.SIsMember(ctx, key, value).Result() } // Smembers is the implementation of redis smembers command. @@ -1781,18 +1435,13 @@ func (s *Redis) Smembers(key string) ([]string, error) { } // SmembersCtx is the implementation of redis smembers command. -func (s *Redis) SmembersCtx(ctx context.Context, key string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SmembersCtx(ctx context.Context, key string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.SMembers(ctx, key).Result() - return err - }, acceptable) - - return + return conn.SMembers(ctx, key).Result() } // Spop is the implementation of redis spop command. @@ -1801,18 +1450,13 @@ func (s *Redis) Spop(key string) (string, error) { } // SpopCtx is the implementation of redis spop command. -func (s *Redis) SpopCtx(ctx context.Context, key string) (val string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SpopCtx(ctx context.Context, key string) (string, error) { + conn, err := getRedis(s) + if err != nil { + return "", err + } - val, err = conn.SPop(ctx, key).Result() - return err - }, acceptable) - - return + return conn.SPop(ctx, key).Result() } // Srandmember is the implementation of redis srandmember command. @@ -1821,18 +1465,13 @@ func (s *Redis) Srandmember(key string, count int) ([]string, error) { } // SrandmemberCtx is the implementation of redis srandmember command. -func (s *Redis) SrandmemberCtx(ctx context.Context, key string, count int) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SrandmemberCtx(ctx context.Context, key string, count int) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.SRandMemberN(ctx, key, int64(count)).Result() - return err - }, acceptable) - - return + return conn.SRandMemberN(ctx, key, int64(count)).Result() } // Srem is the implementation of redis srem command. @@ -1841,23 +1480,18 @@ func (s *Redis) Srem(key string, values ...any) (int, error) { } // SremCtx is the implementation of redis srem command. -func (s *Redis) SremCtx(ctx context.Context, key string, values ...any) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SremCtx(ctx context.Context, key string, values ...any) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SRem(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.SRem(ctx, key, values...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // String returns the string representation of s. @@ -1871,18 +1505,13 @@ func (s *Redis) Sunion(keys ...string) ([]string, error) { } // SunionCtx is the implementation of redis sunion command. -func (s *Redis) SunionCtx(ctx context.Context, keys ...string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SunionCtx(ctx context.Context, keys ...string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.SUnion(ctx, keys...).Result() - return err - }, acceptable) - - return + return conn.SUnion(ctx, keys...).Result() } // Sunionstore is the implementation of redis sunionstore command. @@ -1892,23 +1521,18 @@ func (s *Redis) Sunionstore(destination string, keys ...string) (int, error) { // SunionstoreCtx is the implementation of redis sunionstore command. func (s *Redis) SunionstoreCtx(ctx context.Context, destination string, keys ...string) ( - val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SUnionStore(ctx, destination, keys...).Result() - if err != nil { - return err - } + v, err := conn.SUnionStore(ctx, destination, keys...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Sdiff is the implementation of redis sdiff command. @@ -1917,18 +1541,13 @@ func (s *Redis) Sdiff(keys ...string) ([]string, error) { } // SdiffCtx is the implementation of redis sdiff command. -func (s *Redis) SdiffCtx(ctx context.Context, keys ...string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SdiffCtx(ctx context.Context, keys ...string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.SDiff(ctx, keys...).Result() - return err - }, acceptable) - - return + return conn.SDiff(ctx, keys...).Result() } // Sdiffstore is the implementation of redis sdiffstore command. @@ -1938,23 +1557,18 @@ func (s *Redis) Sdiffstore(destination string, keys ...string) (int, error) { // SdiffstoreCtx is the implementation of redis sdiffstore command. func (s *Redis) SdiffstoreCtx(ctx context.Context, destination string, keys ...string) ( - val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SDiffStore(ctx, destination, keys...).Result() - if err != nil { - return err - } + v, err := conn.SDiffStore(ctx, destination, keys...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Sinter is the implementation of redis sinter command. @@ -1963,18 +1577,13 @@ func (s *Redis) Sinter(keys ...string) ([]string, error) { } // SinterCtx is the implementation of redis sinter command. -func (s *Redis) SinterCtx(ctx context.Context, keys ...string) (val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) SinterCtx(ctx context.Context, keys ...string) ([]string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.SInter(ctx, keys...).Result() - return err - }, acceptable) - - return + return conn.SInter(ctx, keys...).Result() } // Sinterstore is the implementation of redis sinterstore command. @@ -1984,23 +1593,18 @@ func (s *Redis) Sinterstore(destination string, keys ...string) (int, error) { // SinterstoreCtx is the implementation of redis sinterstore command. func (s *Redis) SinterstoreCtx(ctx context.Context, destination string, keys ...string) ( - val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.SInterStore(ctx, destination, keys...).Result() - if err != nil { - return err - } + v, err := conn.SInterStore(ctx, destination, keys...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Ttl is the implementation of redis ttl command. @@ -2009,29 +1613,24 @@ func (s *Redis) Ttl(key string) (int, error) { } // TtlCtx is the implementation of redis ttl command. -func (s *Redis) TtlCtx(ctx context.Context, key string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) TtlCtx(ctx context.Context, key string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - duration, err := conn.TTL(ctx, key).Result() - if err != nil { - return err - } + duration, err := conn.TTL(ctx, key).Result() + if err != nil { + return 0, err + } - if duration >= 0 { - val = int(duration / time.Second) - } else { - // -2 means key does not exist - // -1 means key exists but has no expire - val = int(duration) - } - return nil - }, acceptable) + if duration >= 0 { + return int(duration / time.Second), nil + } - return + // -2 means key does not exist + // -1 means key exists but has no expire + return int(duration), nil } // Zadd is the implementation of redis zadd command. @@ -2040,8 +1639,7 @@ func (s *Redis) Zadd(key string, score int64, value string) (bool, error) { } // ZaddCtx is the implementation of redis zadd command. -func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value string) ( - val bool, err error) { +func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value string) (bool, error) { return s.ZaddFloatCtx(ctx, key, float64(score), value) } @@ -2052,26 +1650,55 @@ func (s *Redis) ZaddFloat(key string, score float64, value string) (bool, error) // ZaddFloatCtx is the implementation of redis zadd command. func (s *Redis) ZaddFloatCtx(ctx context.Context, key string, score float64, value string) ( - val bool, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } - v, err := conn.ZAdd(ctx, key, red.Z{ - Score: score, - Member: value, - }).Result() - if err != nil { - return err - } + v, err := conn.ZAdd(ctx, key, red.Z{ + Score: score, + Member: value, + }).Result() + if err != nil { + return false, err + } - val = v == 1 - return nil - }, acceptable) + return v == 1, nil +} - return +// Zaddnx is the implementation of redis zadd nx command. +func (s *Redis) Zaddnx(key string, score int64, value string) (bool, error) { + return s.ZaddnxCtx(context.Background(), key, score, value) +} + +// ZaddnxCtx is the implementation of redis zadd nx command. +func (s *Redis) ZaddnxCtx(ctx context.Context, key string, score int64, value string) (bool, error) { + return s.ZaddnxFloatCtx(ctx, key, float64(score), value) +} + +// ZaddnxFloat is the implementation of redis zaddnx command. +func (s *Redis) ZaddnxFloat(key string, score float64, value string) (bool, error) { + return s.ZaddFloatCtx(context.Background(), key, score, value) +} + +// ZaddnxFloatCtx is the implementation of redis zaddnx command. +func (s *Redis) ZaddnxFloatCtx(ctx context.Context, key string, score float64, value string) ( + bool, error) { + conn, err := getRedis(s) + if err != nil { + return false, err + } + + v, err := conn.ZAddNX(ctx, key, red.Z{ + Score: score, + Member: value, + }).Result() + if err != nil { + return false, err + } + + return v == 1, nil } // Zadds is the implementation of redis zadds command. @@ -2080,29 +1707,19 @@ func (s *Redis) Zadds(key string, ps ...Pair) (int64, error) { } // ZaddsCtx is the implementation of redis zadds command. -func (s *Redis) ZaddsCtx(ctx context.Context, key string, ps ...Pair) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZaddsCtx(ctx context.Context, key string, ps ...Pair) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - var zs []red.Z - for _, p := range ps { - z := red.Z{Score: float64(p.Score), Member: p.Key} - zs = append(zs, z) - } + var zs []red.Z + for _, p := range ps { + z := red.Z{Score: float64(p.Score), Member: p.Key} + zs = append(zs, z) + } - v, err := conn.ZAdd(ctx, key, zs...).Result() - if err != nil { - return err - } - - val = v - return nil - }, acceptable) - - return + return conn.ZAdd(ctx, key, zs...).Result() } // Zcard is the implementation of redis zcard command. @@ -2111,23 +1728,18 @@ func (s *Redis) Zcard(key string) (int, error) { } // ZcardCtx is the implementation of redis zcard command. -func (s *Redis) ZcardCtx(ctx context.Context, key string) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZcardCtx(ctx context.Context, key string) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZCard(ctx, key).Result() - if err != nil { - return err - } + v, err := conn.ZCard(ctx, key).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Zcount is the implementation of redis zcount command. @@ -2136,24 +1748,19 @@ func (s *Redis) Zcount(key string, start, stop int64) (int, error) { } // ZcountCtx is the implementation of redis zcount command. -func (s *Redis) ZcountCtx(ctx context.Context, key string, start, stop int64) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZcountCtx(ctx context.Context, key string, start, stop int64) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZCount(ctx, key, strconv.FormatInt(start, 10), - strconv.FormatInt(stop, 10)).Result() - if err != nil { - return err - } + v, err := conn.ZCount(ctx, key, strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10)).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Zincrby is the implementation of redis zincrby command. @@ -2163,23 +1770,18 @@ func (s *Redis) Zincrby(key string, increment int64, field string) (int64, error // ZincrbyCtx is the implementation of redis zincrby command. func (s *Redis) ZincrbyCtx(ctx context.Context, key string, increment int64, field string) ( - val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZIncrBy(ctx, key, float64(increment), field).Result() - if err != nil { - return err - } + v, err := conn.ZIncrBy(ctx, key, float64(increment), field).Result() + if err != nil { + return 0, err + } - val = int64(v) - return nil - }, acceptable) - - return + return int64(v), nil } // Zscore is the implementation of redis zscore command. @@ -2188,23 +1790,18 @@ func (s *Redis) Zscore(key, value string) (int64, error) { } // ZscoreCtx is the implementation of redis zscore command. -func (s *Redis) ZscoreCtx(ctx context.Context, key, value string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZscoreCtx(ctx context.Context, key, value string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZScore(ctx, key, value).Result() - if err != nil { - return err - } + v, err := conn.ZScore(ctx, key, value).Result() + if err != nil { + return 0, err + } - val = int64(v) - return nil - }, acceptable) - - return + return int64(v), nil } // ZscoreByFloat is the implementation of redis zscore command score by float. @@ -2213,39 +1810,30 @@ func (s *Redis) ZscoreByFloat(key, value string) (float64, error) { } // ZscoreByFloatCtx is the implementation of redis zscore command score by float. -func (s *Redis) ZscoreByFloatCtx(ctx context.Context, key, value string) (val float64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } - val, err = conn.ZScore(ctx, key, value).Result() - return err - }, acceptable) +func (s *Redis) ZscoreByFloatCtx(ctx context.Context, key, value string) (float64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - return + return conn.ZScore(ctx, key, value).Result() } // Zscan is the implementation of redis zscan command. func (s *Redis) Zscan(key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { + []string, uint64, error) { return s.ZscanCtx(context.Background(), key, cursor, match, count) } // ZscanCtx is the implementation of redis zscan command. func (s *Redis) ZscanCtx(ctx context.Context, key string, cursor uint64, match string, count int64) ( - keys []string, cur uint64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, uint64, error) { + conn, err := getRedis(s) + if err != nil { + return nil, 0, err + } - keys, cur, err = conn.ZScan(ctx, key, cursor, match, count).Result() - return err - }, acceptable) - - return + return conn.ZScan(ctx, key, cursor, match, count).Result() } // Zrank is the implementation of redis zrank command. @@ -2254,18 +1842,13 @@ func (s *Redis) Zrank(key, field string) (int64, error) { } // ZrankCtx is the implementation of redis zrank command. -func (s *Redis) ZrankCtx(ctx context.Context, key, field string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZrankCtx(ctx context.Context, key, field string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.ZRank(ctx, key, field).Result() - return err - }, acceptable) - - return + return conn.ZRank(ctx, key, field).Result() } // Zrem is the implementation of redis zrem command. @@ -2274,23 +1857,18 @@ func (s *Redis) Zrem(key string, values ...any) (int, error) { } // ZremCtx is the implementation of redis zrem command. -func (s *Redis) ZremCtx(ctx context.Context, key string, values ...any) (val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZremCtx(ctx context.Context, key string, values ...any) (int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZRem(ctx, key, values...).Result() - if err != nil { - return err - } + v, err := conn.ZRem(ctx, key, values...).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Zremrangebyscore is the implementation of redis zremrangebyscore command. @@ -2300,24 +1878,19 @@ func (s *Redis) Zremrangebyscore(key string, start, stop int64) (int, error) { // ZremrangebyscoreCtx is the implementation of redis zremrangebyscore command. func (s *Redis) ZremrangebyscoreCtx(ctx context.Context, key string, start, stop int64) ( - val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZRemRangeByScore(ctx, key, strconv.FormatInt(start, 10), - strconv.FormatInt(stop, 10)).Result() - if err != nil { - return err - } + v, err := conn.ZRemRangeByScore(ctx, key, strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10)).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Zremrangebyrank is the implementation of redis zremrangebyrank command. @@ -2327,23 +1900,18 @@ func (s *Redis) Zremrangebyrank(key string, start, stop int64) (int, error) { // ZremrangebyrankCtx is the implementation of redis zremrangebyrank command. func (s *Redis) ZremrangebyrankCtx(ctx context.Context, key string, start, stop int64) ( - val int, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - v, err := conn.ZRemRangeByRank(ctx, key, start, stop).Result() - if err != nil { - return err - } + v, err := conn.ZRemRangeByRank(ctx, key, start, stop).Result() + if err != nil { + return 0, err + } - val = int(v) - return nil - }, acceptable) - - return + return int(v), nil } // Zrange is the implementation of redis zrange command. @@ -2353,18 +1921,13 @@ func (s *Redis) Zrange(key string, start, stop int64) ([]string, error) { // ZrangeCtx is the implementation of redis zrange command. func (s *Redis) ZrangeCtx(ctx context.Context, key string, start, stop int64) ( - val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.ZRange(ctx, key, start, stop).Result() - return err - }, acceptable) - - return + return conn.ZRange(ctx, key, start, stop).Result() } // ZrangeWithScores is the implementation of redis zrange command with scores. @@ -2374,23 +1937,18 @@ func (s *Redis) ZrangeWithScores(key string, start, stop int64) ([]Pair, error) // ZrangeWithScoresCtx is the implementation of redis zrange command with scores. func (s *Redis) ZrangeWithScoresCtx(ctx context.Context, key string, start, stop int64) ( - val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []Pair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeWithScores(ctx, key, start, stop).Result() - if err != nil { - return err - } + v, err := conn.ZRangeWithScores(ctx, key, start, stop).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZrangeWithScoresByFloat is the implementation of redis zrange command with scores by float64. @@ -2400,23 +1958,18 @@ func (s *Redis) ZrangeWithScoresByFloat(key string, start, stop int64) ([]FloatP // ZrangeWithScoresByFloatCtx is the implementation of redis zrange command with scores by float64. func (s *Redis) ZrangeWithScoresByFloatCtx(ctx context.Context, key string, start, stop int64) ( - val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []FloatPair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeWithScores(ctx, key, start, stop).Result() - if err != nil { - return err - } + v, err := conn.ZRangeWithScores(ctx, key, start, stop).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // ZRevRangeWithScores is the implementation of redis zrevrange command with scores. @@ -2433,29 +1986,24 @@ func (s *Redis) ZrevrangeWithScores(key string, start, stop int64) ([]Pair, erro // ZRevRangeWithScoresCtx is the implementation of redis zrevrange command with scores. // Deprecated: use ZrevrangeWithScoresCtx instead. func (s *Redis) ZRevRangeWithScoresCtx(ctx context.Context, key string, start, stop int64) ( - val []Pair, err error) { + []Pair, error) { return s.ZrevrangeWithScoresCtx(ctx, key, start, stop) } // ZrevrangeWithScoresCtx is the implementation of redis zrevrange command with scores. func (s *Redis) ZrevrangeWithScoresCtx(ctx context.Context, key string, start, stop int64) ( - val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []Pair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeWithScores(ctx, key, start, stop).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeWithScores(ctx, key, start, stop).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZRevRangeWithScoresByFloat is the implementation of redis zrevrange command with scores by float. @@ -2472,29 +2020,24 @@ func (s *Redis) ZrevrangeWithScoresByFloat(key string, start, stop int64) ([]Flo // ZRevRangeWithScoresByFloatCtx is the implementation of redis zrevrange command with scores by float. // Deprecated: use ZrevrangeWithScoresByFloatCtx instead. func (s *Redis) ZRevRangeWithScoresByFloatCtx(ctx context.Context, key string, start, stop int64) ( - val []FloatPair, err error) { + []FloatPair, error) { return s.ZrevrangeWithScoresByFloatCtx(ctx, key, start, stop) } // ZrevrangeWithScoresByFloatCtx is the implementation of redis zrevrange command with scores by float. func (s *Redis) ZrevrangeWithScoresByFloatCtx(ctx context.Context, key string, start, stop int64) ( - val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []FloatPair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeWithScores(ctx, key, start, stop).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeWithScores(ctx, key, start, stop).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // ZrangebyscoreWithScores is the implementation of redis zrangebyscore command with scores. @@ -2504,55 +2047,46 @@ func (s *Redis) ZrangebyscoreWithScores(key string, start, stop int64) ([]Pair, // ZrangebyscoreWithScoresCtx is the implementation of redis zrangebyscore command with scores. func (s *Redis) ZrangebyscoreWithScoresCtx(ctx context.Context, key string, start, stop int64) ( - val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []Pair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatInt(start, 10), - Max: strconv.FormatInt(stop, 10), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatInt(start, 10), + Max: strconv.FormatInt(stop, 10), + }).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZrangebyscoreWithScoresByFloat is the implementation of redis zrangebyscore command with scores by float. -func (s *Redis) ZrangebyscoreWithScoresByFloat(key string, start, stop float64) ([]FloatPair, error) { +func (s *Redis) ZrangebyscoreWithScoresByFloat(key string, start, stop float64) ( + []FloatPair, error) { return s.ZrangebyscoreWithScoresByFloatCtx(context.Background(), key, start, stop) } // ZrangebyscoreWithScoresByFloatCtx is the implementation of redis zrangebyscore command with scores by float. func (s *Redis) ZrangebyscoreWithScoresByFloatCtx(ctx context.Context, key string, start, stop float64) ( - val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []FloatPair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatFloat(start, 'f', -1, 64), - Max: strconv.FormatFloat(stop, 'f', -1, 64), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatFloat(start, 'f', -1, 64), + Max: strconv.FormatFloat(stop, 'f', -1, 64), + }).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // ZrangebyscoreWithScoresAndLimit is the implementation of redis zrangebyscore command @@ -2565,32 +2099,27 @@ func (s *Redis) ZrangebyscoreWithScoresAndLimit(key string, start, stop int64, // ZrangebyscoreWithScoresAndLimitCtx is the implementation of redis zrangebyscore command // with scores and limit. func (s *Redis) ZrangebyscoreWithScoresAndLimitCtx(ctx context.Context, key string, start, - stop int64, page, size int) (val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - if size <= 0 { - return nil - } + stop int64, page, size int) ([]Pair, error) { + if size <= 0 { + return nil, nil + } - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatInt(start, 10), - Max: strconv.FormatInt(stop, 10), - Offset: int64(page * size), - Count: int64(size), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatInt(start, 10), + Max: strconv.FormatInt(stop, 10), + Offset: int64(page * size), + Count: int64(size), + }).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZrangebyscoreWithScoresByFloatAndLimit is the implementation of redis zrangebyscore command @@ -2604,32 +2133,27 @@ func (s *Redis) ZrangebyscoreWithScoresByFloatAndLimit(key string, start, stop f // ZrangebyscoreWithScoresByFloatAndLimitCtx is the implementation of redis zrangebyscore command // with scores by float and limit. func (s *Redis) ZrangebyscoreWithScoresByFloatAndLimitCtx(ctx context.Context, key string, start, - stop float64, page, size int) (val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - if size <= 0 { - return nil - } + stop float64, page, size int) ([]FloatPair, error) { + if size <= 0 { + return nil, nil + } - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatFloat(start, 'f', -1, 64), - Max: strconv.FormatFloat(stop, 'f', -1, 64), - Offset: int64(page * size), - Count: int64(size), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatFloat(start, 'f', -1, 64), + Max: strconv.FormatFloat(stop, 'f', -1, 64), + Offset: int64(page * size), + Count: int64(size), + }).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // Zrevrange is the implementation of redis zrevrange command. @@ -2639,18 +2163,13 @@ func (s *Redis) Zrevrange(key string, start, stop int64) ([]string, error) { // ZrevrangeCtx is the implementation of redis zrevrange command. func (s *Redis) ZrevrangeCtx(ctx context.Context, key string, start, stop int64) ( - val []string, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []string, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - val, err = conn.ZRevRange(ctx, key, start, stop).Result() - return err - }, acceptable) - - return + return conn.ZRevRange(ctx, key, start, stop).Result() } // ZrevrangebyscoreWithScores is the implementation of redis zrevrangebyscore command with scores. @@ -2660,26 +2179,21 @@ func (s *Redis) ZrevrangebyscoreWithScores(key string, start, stop int64) ([]Pai // ZrevrangebyscoreWithScoresCtx is the implementation of redis zrevrangebyscore command with scores. func (s *Redis) ZrevrangebyscoreWithScoresCtx(ctx context.Context, key string, start, stop int64) ( - val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + []Pair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatInt(start, 10), - Max: strconv.FormatInt(stop, 10), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatInt(start, 10), + Max: strconv.FormatInt(stop, 10), + }).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZrevrangebyscoreWithScoresByFloat is the implementation of redis zrevrangebyscore command with scores by float. @@ -2690,26 +2204,21 @@ func (s *Redis) ZrevrangebyscoreWithScoresByFloat(key string, start, stop float6 // ZrevrangebyscoreWithScoresByFloatCtx is the implementation of redis zrevrangebyscore command with scores by float. func (s *Redis) ZrevrangebyscoreWithScoresByFloatCtx(ctx context.Context, key string, - start, stop float64) (val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + start, stop float64) ([]FloatPair, error) { + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatFloat(start, 'f', -1, 64), - Max: strconv.FormatFloat(stop, 'f', -1, 64), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatFloat(start, 'f', -1, 64), + Max: strconv.FormatFloat(stop, 'f', -1, 64), + }).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // ZrevrangebyscoreWithScoresAndLimit is the implementation of redis zrevrangebyscore command @@ -2723,32 +2232,27 @@ func (s *Redis) ZrevrangebyscoreWithScoresAndLimit(key string, start, stop int64 // ZrevrangebyscoreWithScoresAndLimitCtx is the implementation of redis zrevrangebyscore command // with scores and limit. func (s *Redis) ZrevrangebyscoreWithScoresAndLimitCtx(ctx context.Context, key string, - start, stop int64, page, size int) (val []Pair, err error) { - err = s.brk.DoWithAcceptable(func() error { - if size <= 0 { - return nil - } + start, stop int64, page, size int) ([]Pair, error) { + if size <= 0 { + return nil, nil + } - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatInt(start, 10), - Max: strconv.FormatInt(stop, 10), - Offset: int64(page * size), - Count: int64(size), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatInt(start, 10), + Max: strconv.FormatInt(stop, 10), + Offset: int64(page * size), + Count: int64(size), + }).Result() + if err != nil { + return nil, err + } - val = toPairs(v) - return nil - }, acceptable) - - return + return toPairs(v), nil } // ZrevrangebyscoreWithScoresByFloatAndLimit is the implementation of redis zrevrangebyscore command @@ -2762,32 +2266,27 @@ func (s *Redis) ZrevrangebyscoreWithScoresByFloatAndLimit(key string, start, sto // ZrevrangebyscoreWithScoresByFloatAndLimitCtx is the implementation of redis zrevrangebyscore command // with scores by float and limit. func (s *Redis) ZrevrangebyscoreWithScoresByFloatAndLimitCtx(ctx context.Context, key string, - start, stop float64, page, size int) (val []FloatPair, err error) { - err = s.brk.DoWithAcceptable(func() error { - if size <= 0 { - return nil - } + start, stop float64, page, size int) ([]FloatPair, error) { + if size <= 0 { + return nil, nil + } - conn, err := getRedis(s) - if err != nil { - return err - } + conn, err := getRedis(s) + if err != nil { + return nil, err + } - v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ - Min: strconv.FormatFloat(start, 'f', -1, 64), - Max: strconv.FormatFloat(stop, 'f', -1, 64), - Offset: int64(page * size), - Count: int64(size), - }).Result() - if err != nil { - return err - } + v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{ + Min: strconv.FormatFloat(start, 'f', -1, 64), + Max: strconv.FormatFloat(stop, 'f', -1, 64), + Offset: int64(page * size), + Count: int64(size), + }).Result() + if err != nil { + return nil, err + } - val = toFloatPairs(v) - return nil - }, acceptable) - - return + return toFloatPairs(v), nil } // Zrevrank is the implementation of redis zrevrank command. @@ -2796,18 +2295,13 @@ func (s *Redis) Zrevrank(key, field string) (int64, error) { } // ZrevrankCtx is the implementation of redis zrevrank command. -func (s *Redis) ZrevrankCtx(ctx context.Context, key, field string) (val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } +func (s *Redis) ZrevrankCtx(ctx context.Context, key, field string) (int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.ZRevRank(ctx, key, field).Result() - return err - }, acceptable) - - return + return conn.ZRevRank(ctx, key, field).Result() } // Zunionstore is the implementation of redis zunionstore command. @@ -2817,18 +2311,13 @@ func (s *Redis) Zunionstore(dest string, store *ZStore) (int64, error) { // ZunionstoreCtx is the implementation of redis zunionstore command. func (s *Redis) ZunionstoreCtx(ctx context.Context, dest string, store *ZStore) ( - val int64, err error) { - err = s.brk.DoWithAcceptable(func() error { - conn, err := getRedis(s) - if err != nil { - return err - } + int64, error) { + conn, err := getRedis(s) + if err != nil { + return 0, err + } - val, err = conn.ZUnionStore(ctx, dest, store).Result() - return err - }, acceptable) - - return + return conn.ZUnionStore(ctx, dest, store).Result() } func (s *Redis) checkConnection(pingTimeout time.Duration) error { @@ -2874,7 +2363,7 @@ func WithTLS() Option { } } -// withHook customizes the given Redis with given hook, only for private use now, +// withHook customizes the given Redis with given durationHook, only for private use now, // maybe expose later. func withHook(hook red.Hook) Option { return func(r *Redis) { @@ -2883,7 +2372,7 @@ func withHook(hook red.Hook) Option { } func acceptable(err error) bool { - return err == nil || err == red.Nil || errors.Is(err, context.Canceled) + return err == nil || errors.Is(err, red.Nil) || errors.Is(err, context.Canceled) } func getRedis(r *Redis) (RedisNode, error) { diff --git a/core/stores/redis/redis_test.go b/core/stores/redis/redis_test.go index b455523c..4ddd2bfd 100644 --- a/core/stores/redis/redis_test.go +++ b/core/stores/redis/redis_test.go @@ -36,7 +36,7 @@ func (m myHook) ProcessHook(next red.ProcessHook) red.ProcessHook { if cmd.Name() == "ping" && !m.includePing { return next(ctx, cmd) } - return errors.New("hook error") + return errors.New("durationHook error") } } @@ -155,7 +155,7 @@ func TestRedis_NonBlock(t *testing.T) { t.Run("nonBlock true", func(t *testing.T) { s := miniredis.RunT(t) - // use hook to simulate redis ping error + // use durationHook to simulate redis ping error _, err := NewRedis(RedisConf{ Host: s.Addr(), NonBlock: true, @@ -177,7 +177,7 @@ func TestRedis_NonBlock(t *testing.T) { func TestRedis_Decr(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Decr("a") + _, err := newRedis(client.Addr, badType()).Decr("a") assert.NotNil(t, err) val, err := client.Decr("a") assert.Nil(t, err) @@ -190,7 +190,7 @@ func TestRedis_Decr(t *testing.T) { func TestRedis_DecrBy(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Decrby("a", 2) + _, err := newRedis(client.Addr, badType()).Decrby("a", 2) assert.NotNil(t, err) val, err := client.Decrby("a", 2) assert.Nil(t, err) @@ -203,7 +203,7 @@ func TestRedis_DecrBy(t *testing.T) { func TestRedis_Exists(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Exists("a") + _, err := newRedis(client.Addr, badType()).Exists("a") assert.NotNil(t, err) ok, err := client.Exists("a") assert.Nil(t, err) @@ -217,7 +217,7 @@ func TestRedis_Exists(t *testing.T) { func TestRedisTLS_Exists(t *testing.T) { runOnRedisTLS(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Exists("a") + _, err := newRedis(client.Addr, badType()).Exists("a") assert.NotNil(t, err) ok, err := client.Exists("a") assert.NotNil(t, err) @@ -232,7 +232,7 @@ func TestRedisTLS_Exists(t *testing.T) { func TestRedis_ExistsMany(t *testing.T) { runOnRedis(t, func(client *Redis) { // Attempt to create a new Redis instance with an incorrect type and call ExistsMany - _, err := New(client.Addr, badType()).ExistsMany("key1", "key2") + _, err := newRedis(client.Addr, badType()).ExistsMany("key1", "key2") assert.NotNil(t, err) // Check if key1 and key2 exist, expecting that they do not @@ -261,7 +261,7 @@ func TestRedis_ExistsMany(t *testing.T) { func TestRedis_Eval(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"}) + _, err := newRedis(client.Addr, badType()).Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"}) assert.NotNil(t, err) _, err = client.Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"}) assert.Equal(t, Nil, err) @@ -279,7 +279,7 @@ func TestRedis_ScriptRun(t *testing.T) { runOnRedis(t, func(client *Redis) { sc := NewScript(`redis.call("EXISTS", KEYS[1])`) sc2 := NewScript(`return redis.call("EXISTS", KEYS[1])`) - _, err := New(client.Addr, badType()).ScriptRun(sc, []string{"notexist"}) + _, err := newRedis(client.Addr, badType()).ScriptRun(sc, []string{"notexist"}) assert.NotNil(t, err) _, err = client.ScriptRun(sc, []string{"notexist"}) assert.Equal(t, Nil, err) @@ -297,7 +297,7 @@ func TestRedis_GeoHash(t *testing.T) { runOnRedis(t, func(client *Redis) { _, err := client.GeoHash("parent", "child1", "child2") assert.Error(t, err) - _, err = New(client.Addr, badType()).GeoHash("parent", "child1", "child2") + _, err = newRedis(client.Addr, badType()).GeoHash("parent", "child1", "child2") assert.Error(t, err) }) } @@ -306,7 +306,7 @@ func TestRedis_Hgetall(t *testing.T) { runOnRedis(t, func(client *Redis) { assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hgetall("a") + _, err := newRedis(client.Addr, badType()).Hgetall("a") assert.NotNil(t, err) vals, err := client.Hgetall("a") assert.Nil(t, err) @@ -319,10 +319,10 @@ func TestRedis_Hgetall(t *testing.T) { func TestRedis_Hvals(t *testing.T) { runOnRedis(t, func(client *Redis) { - assert.NotNil(t, New(client.Addr, badType()).Hset("a", "aa", "aaa")) + assert.NotNil(t, newRedis(client.Addr, badType()).Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hvals("a") + _, err := newRedis(client.Addr, badType()).Hvals("a") assert.NotNil(t, err) vals, err := client.Hvals("a") assert.Nil(t, err) @@ -334,7 +334,7 @@ func TestRedis_Hsetnx(t *testing.T) { runOnRedis(t, func(client *Redis) { assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hsetnx("a", "bb", "ccc") + _, err := newRedis(client.Addr, badType()).Hsetnx("a", "bb", "ccc") assert.NotNil(t, err) ok, err := client.Hsetnx("a", "bb", "ccc") assert.Nil(t, err) @@ -352,7 +352,7 @@ func TestRedis_HdelHlen(t *testing.T) { runOnRedis(t, func(client *Redis) { assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hlen("a") + _, err := newRedis(client.Addr, badType()).Hlen("a") assert.NotNil(t, err) num, err := client.Hlen("a") assert.Nil(t, err) @@ -368,7 +368,7 @@ func TestRedis_HdelHlen(t *testing.T) { func TestRedis_HIncrBy(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Hincrby("key", "field", 2) + _, err := newRedis(client.Addr, badType()).Hincrby("key", "field", 2) assert.NotNil(t, err) val, err := client.Hincrby("key", "field", 2) assert.Nil(t, err) @@ -383,7 +383,7 @@ func TestRedis_Hkeys(t *testing.T) { runOnRedis(t, func(client *Redis) { assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hkeys("a") + _, err := newRedis(client.Addr, badType()).Hkeys("a") assert.NotNil(t, err) vals, err := client.Hkeys("a") assert.Nil(t, err) @@ -395,7 +395,7 @@ func TestRedis_Hmget(t *testing.T) { runOnRedis(t, func(client *Redis) { assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "bb", "bbb")) - _, err := New(client.Addr, badType()).Hmget("a", "aa", "bb") + _, err := newRedis(client.Addr, badType()).Hmget("a", "aa", "bb") assert.NotNil(t, err) vals, err := client.Hmget("a", "aa", "bb") assert.Nil(t, err) @@ -408,7 +408,7 @@ func TestRedis_Hmget(t *testing.T) { func TestRedis_Hmset(t *testing.T) { runOnRedis(t, func(client *Redis) { - assert.NotNil(t, New(client.Addr, badType()).Hmset("a", nil)) + assert.NotNil(t, newRedis(client.Addr, badType()).Hmset("a", nil)) assert.Nil(t, client.Hmset("a", map[string]string{ "aa": "aaa", "bb": "bbb", @@ -433,7 +433,7 @@ func TestRedis_Hscan(t *testing.T) { var cursor uint64 = 0 sum := 0 for { - _, _, err := New(client.Addr, badType()).Hscan(key, cursor, "*", 100) + _, _, err := newRedis(client.Addr, badType()).Hscan(key, cursor, "*", 100) assert.NotNil(t, err) reMap, next, err := client.Hscan(key, cursor, "*", 100) assert.Nil(t, err) @@ -445,7 +445,7 @@ func TestRedis_Hscan(t *testing.T) { } assert.Equal(t, sum, 3100) - _, err = New(client.Addr, badType()).Del(key) + _, err = newRedis(client.Addr, badType()).Del(key) assert.Error(t, err) _, err = client.Del(key) assert.NoError(t, err) @@ -462,7 +462,7 @@ func TestRedis_Hscan(t *testing.T) { func TestRedis_Incr(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Incr("a") + _, err := newRedis(client.Addr, badType()).Incr("a") assert.NotNil(t, err) val, err := client.Incr("a") assert.Nil(t, err) @@ -475,7 +475,7 @@ func TestRedis_Incr(t *testing.T) { func TestRedis_IncrBy(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Incrby("a", 2) + _, err := newRedis(client.Addr, badType()).Incrby("a", 2) assert.NotNil(t, err) val, err := client.Incrby("a", 2) assert.Nil(t, err) @@ -492,7 +492,7 @@ func TestRedis_Keys(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "value2") assert.Nil(t, err) - _, err = New(client.Addr, badType()).Keys("*") + _, err = newRedis(client.Addr, badType()).Keys("*") assert.NotNil(t, err) keys, err := client.Keys("*") assert.Nil(t, err) @@ -504,13 +504,13 @@ func TestRedis_HyperLogLog(t *testing.T) { t.Run("hyperloglog", func(t *testing.T) { runOnRedis(t, func(client *Redis) { client.Ping() - r := New(client.Addr) - _, err := New(client.Addr, badType()).Pfadd("key1", "val1") + r := newRedis(client.Addr) + _, err := newRedis(client.Addr, badType()).Pfadd("key1", "val1") assert.Error(t, err) ok, err := r.Pfadd("key1", "val1") assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Pfcount("key1") + _, err = newRedis(client.Addr, badType()).Pfcount("key1") assert.Error(t, err) val, err := r.Pfcount("key1") assert.Nil(t, err) @@ -521,7 +521,7 @@ func TestRedis_HyperLogLog(t *testing.T) { val, err = r.Pfcount("key2") assert.Nil(t, err) assert.Equal(t, int64(1), val) - err = New(client.Addr, badType()).Pfmerge("key3", "key1", "key2") + err = newRedis(client.Addr, badType()).Pfmerge("key3", "key1", "key2") assert.Error(t, err) err = r.Pfmerge("key1", "key2") assert.Nil(t, err) @@ -542,22 +542,22 @@ func TestRedis_HyperLogLog(t *testing.T) { func TestRedis_List(t *testing.T) { t.Run("list", func(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Lpush("key", "value1", "value2") + _, err := newRedis(client.Addr, badType()).Lpush("key", "value1", "value2") assert.NotNil(t, err) val, err := client.Lpush("key", "value1", "value2") assert.Nil(t, err) assert.Equal(t, 2, val) - _, err = New(client.Addr, badType()).Rpush("key", "value3", "value4") + _, err = newRedis(client.Addr, badType()).Rpush("key", "value3", "value4") assert.NotNil(t, err) val, err = client.Rpush("key", "value3", "value4") assert.Nil(t, err) assert.Equal(t, 4, val) - _, err = New(client.Addr, badType()).Llen("key") + _, err = newRedis(client.Addr, badType()).Llen("key") assert.NotNil(t, err) val, err = client.Llen("key") assert.Nil(t, err) assert.Equal(t, 4, val) - _, err = New(client.Addr, badType()).Lindex("key", 1) + _, err = newRedis(client.Addr, badType()).Lindex("key", 1) assert.NotNil(t, err) value, err := client.Lindex("key", 0) assert.Nil(t, err) @@ -565,7 +565,7 @@ func TestRedis_List(t *testing.T) { vals, err := client.Lrange("key", 0, 10) assert.Nil(t, err) assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals) - _, err = New(client.Addr, badType()).Lpop("key") + _, err = newRedis(client.Addr, badType()).Lpop("key") assert.NotNil(t, err) v, err := client.Lpop("key") assert.Nil(t, err) @@ -573,7 +573,7 @@ func TestRedis_List(t *testing.T) { val, err = client.Lpush("key", "value1", "value2") assert.Nil(t, err) assert.Equal(t, 5, val) - _, err = New(client.Addr, badType()).Rpop("key") + _, err = newRedis(client.Addr, badType()).Rpop("key") assert.NotNil(t, err) v, err = client.Rpop("key") assert.Nil(t, err) @@ -581,12 +581,12 @@ func TestRedis_List(t *testing.T) { val, err = client.Rpush("key", "value4", "value3", "value3") assert.Nil(t, err) assert.Equal(t, 7, val) - _, err = New(client.Addr, badType()).Lrem("key", 2, "value1") + _, err = newRedis(client.Addr, badType()).Lrem("key", 2, "value1") assert.NotNil(t, err) n, err := client.Lrem("key", 2, "value1") assert.Nil(t, err) assert.Equal(t, 2, n) - _, err = New(client.Addr, badType()).Lrange("key", 0, 10) + _, err = newRedis(client.Addr, badType()).Lrange("key", 0, 10) assert.NotNil(t, err) vals, err = client.Lrange("key", 0, 10) assert.Nil(t, err) @@ -597,7 +597,7 @@ func TestRedis_List(t *testing.T) { vals, err = client.Lrange("key", 0, 10) assert.Nil(t, err) assert.EqualValues(t, []string{"value2", "value3", "value4"}, vals) - err = New(client.Addr, badType()).Ltrim("key", 0, 1) + err = newRedis(client.Addr, badType()).Ltrim("key", 0, 1) assert.Error(t, err) err = client.Ltrim("key", 0, 1) assert.Nil(t, err) @@ -660,6 +660,33 @@ func TestRedis_List(t *testing.T) { }) } +func TestRedis_Mset(t *testing.T) { + t.Run("mset", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + // Attempt to Mget with a bad client type, expecting an error. + _, err := New(client.Addr, badType()).Mset("key1", "value1") + assert.NotNil(t, err) + + // Set multiple key-value pairs using Mset and expect no error. + _, err = client.Mset("key1", "value1", "key2", "value2") + assert.Nil(t, err) + + // Retrieve the values for the keys set above using Mget and expect no error. + vals, err := client.Mget("key1", "key2") + assert.Nil(t, err) + assert.EqualValues(t, []string{"value1", "value2"}, vals) + }) + }) + + // Test case for Mset operation with an incorrect number of arguments, expecting an error. + t.Run("mset error", func(t *testing.T) { + runOnRedisWithError(t, func(client *Redis) { + _, err := client.Mset("key1", "value1", "key2") + assert.Error(t, err) + }) + }) +} + func TestRedis_Mget(t *testing.T) { t.Run("mget", func(t *testing.T) { runOnRedis(t, func(client *Redis) { @@ -667,7 +694,7 @@ func TestRedis_Mget(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "value2") assert.Nil(t, err) - _, err = New(client.Addr, badType()).Mget("key1", "key0", "key2", "key3") + _, err = newRedis(client.Addr, badType()).Mget("key1", "key0", "key2", "key3") assert.NotNil(t, err) vals, err := client.Mget("key1", "key0", "key2", "key3") assert.Nil(t, err) @@ -686,7 +713,7 @@ func TestRedis_Mget(t *testing.T) { func TestRedis_SetBit(t *testing.T) { t.Run("setbit", func(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).SetBit("key", 1, 1) + _, err := newRedis(client.Addr, badType()).SetBit("key", 1, 1) assert.Error(t, err) val, err := client.SetBit("key", 1, 1) if assert.NoError(t, err) { @@ -709,7 +736,7 @@ func TestRedis_GetBit(t *testing.T) { val, err := client.SetBit("key", 2, 1) assert.Nil(t, err) assert.Equal(t, 0, val) - _, err = New(client.Addr, badType()).GetBit("key", 2) + _, err = newRedis(client.Addr, badType()).GetBit("key", 2) assert.NotNil(t, err) v, err := client.GetBit("key", 2) assert.Nil(t, err) @@ -733,7 +760,7 @@ func TestRedis_BitCount(t *testing.T) { assert.Equal(t, 0, val) } - _, err := New(client.Addr, badType()).BitCount("key", 0, -1) + _, err := newRedis(client.Addr, badType()).BitCount("key", 0, -1) assert.NotNil(t, err) val, err := client.BitCount("key", 0, -1) assert.Nil(t, err) @@ -763,7 +790,7 @@ func TestRedis_BitOpAnd(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "1") assert.Nil(t, err) - _, err = New(client.Addr, badType()).BitOpAnd("destKey", "key1", "key2") + _, err = newRedis(client.Addr, badType()).BitOpAnd("destKey", "key1", "key2") assert.NotNil(t, err) val, err := client.BitOpAnd("destKey", "key1", "key2") assert.Nil(t, err) @@ -779,7 +806,7 @@ func TestRedis_BitOpNot(t *testing.T) { runOnRedis(t, func(client *Redis) { err := client.Set("key1", "\u0000") assert.Nil(t, err) - _, err = New(client.Addr, badType()).BitOpNot("destKey", "key1") + _, err = newRedis(client.Addr, badType()).BitOpNot("destKey", "key1") assert.NotNil(t, err) val, err := client.BitOpNot("destKey", "key1") assert.Nil(t, err) @@ -796,7 +823,7 @@ func TestRedis_BitOpOr(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "0") assert.Nil(t, err) - _, err = New(client.Addr, badType()).BitOpOr("destKey", "key1", "key2") + _, err = newRedis(client.Addr, badType()).BitOpOr("destKey", "key1", "key2") assert.NotNil(t, err) val, err := client.BitOpOr("destKey", "key1", "key2") assert.Nil(t, err) @@ -813,7 +840,7 @@ func TestRedis_BitOpXor(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "\x0f") assert.Nil(t, err) - _, err = New(client.Addr, badType()).BitOpXor("destKey", "key1", "key2") + _, err = newRedis(client.Addr, badType()).BitOpXor("destKey", "key1", "key2") assert.NotNil(t, err) val, err := client.BitOpXor("destKey", "key1", "key2") assert.Nil(t, err) @@ -830,7 +857,7 @@ func TestRedis_BitPos(t *testing.T) { err := client.Set("key", "\xff\xf0\x00") assert.Nil(t, err) - _, err = New(client.Addr, badType()).BitPos("key", 0, 0, -1) + _, err = newRedis(client.Addr, badType()).BitPos("key", 0, 0, -1) assert.NotNil(t, err) val, err := client.BitPos("key", 0, 0, 2) assert.Nil(t, err) @@ -856,7 +883,7 @@ func TestRedis_BitPos(t *testing.T) { func TestRedis_Persist(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Persist("key") + _, err := newRedis(client.Addr, badType()).Persist("key") assert.NotNil(t, err) ok, err := client.Persist("key") assert.Nil(t, err) @@ -866,14 +893,14 @@ func TestRedis_Persist(t *testing.T) { ok, err = client.Persist("key") assert.Nil(t, err) assert.False(t, ok) - err = New(client.Addr, badType()).Expire("key", 5) + err = newRedis(client.Addr, badType()).Expire("key", 5) assert.NotNil(t, err) err = client.Expire("key", 5) assert.Nil(t, err) ok, err = client.Persist("key") assert.Nil(t, err) assert.True(t, ok) - err = New(client.Addr, badType()).Expireat("key", time.Now().Unix()+5) + err = newRedis(client.Addr, badType()).Expireat("key", time.Now().Unix()+5) assert.NotNil(t, err) err = client.Expireat("key", time.Now().Unix()+5) assert.Nil(t, err) @@ -884,9 +911,11 @@ func TestRedis_Persist(t *testing.T) { } func TestRedis_Ping(t *testing.T) { - runOnRedis(t, func(client *Redis) { - ok := client.Ping() - assert.True(t, ok) + t.Run("ping", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + ok := client.Ping() + assert.True(t, ok) + }) }) } @@ -896,7 +925,7 @@ func TestRedis_Scan(t *testing.T) { assert.Nil(t, err) err = client.Set("key2", "value2") assert.Nil(t, err) - _, _, err = New(client.Addr, badType()).Scan(0, "*", 100) + _, _, err = newRedis(client.Addr, badType()).Scan(0, "*", 100) assert.NotNil(t, err) keys, _, err := client.Scan(0, "*", 100) assert.Nil(t, err) @@ -918,7 +947,7 @@ func TestRedis_Sscan(t *testing.T) { var cursor uint64 = 0 sum := 0 for { - _, _, err := New(client.Addr, badType()).Sscan(key, cursor, "", 100) + _, _, err := newRedis(client.Addr, badType()).Sscan(key, cursor, "", 100) assert.NotNil(t, err) keys, next, err := client.Sscan(key, cursor, "", 100) assert.Nil(t, err) @@ -930,7 +959,7 @@ func TestRedis_Sscan(t *testing.T) { } assert.Equal(t, sum, 1550) - _, err = New(client.Addr, badType()).Del(key) + _, err = newRedis(client.Addr, badType()).Del(key) assert.NotNil(t, err) _, err = client.Del(key) assert.Nil(t, err) @@ -940,48 +969,48 @@ func TestRedis_Sscan(t *testing.T) { func TestRedis_Set(t *testing.T) { t.Run("set", func(t *testing.T) { runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).Sadd("key", 1, 2, 3, 4) + _, err := newRedis(client.Addr, badType()).Sadd("key", 1, 2, 3, 4) assert.NotNil(t, err) num, err := client.Sadd("key", 1, 2, 3, 4) assert.Nil(t, err) assert.Equal(t, 4, num) - _, err = New(client.Addr, badType()).Scard("key") + _, err = newRedis(client.Addr, badType()).Scard("key") assert.NotNil(t, err) val, err := client.Scard("key") assert.Nil(t, err) assert.Equal(t, int64(4), val) - _, err = New(client.Addr, badType()).Sismember("key", 2) + _, err = newRedis(client.Addr, badType()).Sismember("key", 2) assert.NotNil(t, err) ok, err := client.Sismember("key", 2) assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Srem("key", 3, 4) + _, err = newRedis(client.Addr, badType()).Srem("key", 3, 4) assert.NotNil(t, err) num, err = client.Srem("key", 3, 4) assert.Nil(t, err) assert.Equal(t, 2, num) - _, err = New(client.Addr, badType()).Smembers("key") + _, err = newRedis(client.Addr, badType()).Smembers("key") assert.NotNil(t, err) vals, err := client.Smembers("key") assert.Nil(t, err) assert.ElementsMatch(t, []string{"1", "2"}, vals) - _, err = New(client.Addr, badType()).Srandmember("key", 1) + _, err = newRedis(client.Addr, badType()).Srandmember("key", 1) assert.NotNil(t, err) members, err := client.Srandmember("key", 1) assert.Nil(t, err) assert.Len(t, members, 1) assert.Contains(t, []string{"1", "2"}, members[0]) - _, err = New(client.Addr, badType()).Spop("key") + _, err = newRedis(client.Addr, badType()).Spop("key") assert.NotNil(t, err) member, err := client.Spop("key") assert.Nil(t, err) assert.Contains(t, []string{"1", "2"}, member) - _, err = New(client.Addr, badType()).Smembers("key") + _, err = newRedis(client.Addr, badType()).Smembers("key") assert.NotNil(t, err) vals, err = client.Smembers("key") assert.Nil(t, err) assert.NotContains(t, vals, member) - _, err = New(client.Addr, badType()).Sadd("key1", 1, 2, 3, 4) + _, err = newRedis(client.Addr, badType()).Sadd("key1", 1, 2, 3, 4) assert.NotNil(t, err) num, err = client.Sadd("key1", 1, 2, 3, 4) assert.Nil(t, err) @@ -989,32 +1018,32 @@ func TestRedis_Set(t *testing.T) { num, err = client.Sadd("key2", 2, 3, 4, 5) assert.Nil(t, err) assert.Equal(t, 4, num) - _, err = New(client.Addr, badType()).Sunion("key1", "key2") + _, err = newRedis(client.Addr, badType()).Sunion("key1", "key2") assert.NotNil(t, err) vals, err = client.Sunion("key1", "key2") assert.Nil(t, err) assert.ElementsMatch(t, []string{"1", "2", "3", "4", "5"}, vals) - _, err = New(client.Addr, badType()).Sunionstore("key3", "key1", "key2") + _, err = newRedis(client.Addr, badType()).Sunionstore("key3", "key1", "key2") assert.NotNil(t, err) num, err = client.Sunionstore("key3", "key1", "key2") assert.Nil(t, err) assert.Equal(t, 5, num) - _, err = New(client.Addr, badType()).Sdiff("key1", "key2") + _, err = newRedis(client.Addr, badType()).Sdiff("key1", "key2") assert.NotNil(t, err) vals, err = client.Sdiff("key1", "key2") assert.Nil(t, err) assert.EqualValues(t, []string{"1"}, vals) - _, err = New(client.Addr, badType()).Sdiffstore("key4", "key1", "key2") + _, err = newRedis(client.Addr, badType()).Sdiffstore("key4", "key1", "key2") assert.NotNil(t, err) num, err = client.Sdiffstore("key4", "key1", "key2") assert.Nil(t, err) assert.Equal(t, 1, num) - _, err = New(client.Addr, badType()).Sinter("key1", "key2") + _, err = newRedis(client.Addr, badType()).Sinter("key1", "key2") assert.NotNil(t, err) vals, err = client.Sinter("key1", "key2") assert.Nil(t, err) assert.ElementsMatch(t, []string{"2", "3", "4"}, vals) - _, err = New(client.Addr, badType()).Sinterstore("key4", "key1", "key2") + _, err = newRedis(client.Addr, badType()).Sinterstore("key4", "key1", "key2") assert.NotNil(t, err) num, err = client.Sinterstore("key4", "key1", "key2") assert.Nil(t, err) @@ -1043,34 +1072,50 @@ func TestRedis_Set(t *testing.T) { } func TestRedis_GetSet(t *testing.T) { - runOnRedis(t, func(client *Redis) { - _, err := New(client.Addr, badType()).GetSet("hello", "world") - assert.NotNil(t, err) - val, err := client.GetSet("hello", "world") - assert.Nil(t, err) - assert.Equal(t, "", val) - val, err = client.Get("hello") - assert.Nil(t, err) - assert.Equal(t, "world", val) - val, err = client.GetSet("hello", "newworld") - assert.Nil(t, err) - assert.Equal(t, "world", val) - val, err = client.Get("hello") - assert.Nil(t, err) - assert.Equal(t, "newworld", val) - ret, err := client.Del("hello") - assert.Nil(t, err) - assert.Equal(t, 1, ret) + t.Run("set_get", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + _, err := newRedis(client.Addr, badType()).GetSet("hello", "world") + assert.NotNil(t, err) + val, err := client.GetSet("hello", "world") + assert.Nil(t, err) + assert.Equal(t, "", val) + val, err = client.Get("hello") + assert.Nil(t, err) + assert.Equal(t, "world", val) + val, err = client.GetSet("hello", "newworld") + assert.Nil(t, err) + assert.Equal(t, "world", val) + val, err = client.Get("hello") + assert.Nil(t, err) + assert.Equal(t, "newworld", val) + ret, err := client.Del("hello") + assert.Nil(t, err) + assert.Equal(t, 1, ret) + }) + }) + + t.Run("set_get with notexists", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + _, err := client.Get("hello") + assert.NoError(t, err) + }) + }) + + t.Run("set_get with error", func(t *testing.T) { + runOnRedisWithError(t, func(client *Redis) { + _, err := client.Get("hello") + assert.Error(t, err) + }) }) } func TestRedis_SetGetDel(t *testing.T) { runOnRedis(t, func(client *Redis) { - err := New(client.Addr, badType()).Set("hello", "world") + err := newRedis(client.Addr, badType()).Set("hello", "world") assert.NotNil(t, err) err = client.Set("hello", "world") assert.Nil(t, err) - _, err = New(client.Addr, badType()).Get("hello") + _, err = newRedis(client.Addr, badType()).Get("hello") assert.NotNil(t, err) val, err := client.Get("hello") assert.Nil(t, err) @@ -1083,11 +1128,11 @@ func TestRedis_SetGetDel(t *testing.T) { func TestRedis_SetExNx(t *testing.T) { runOnRedis(t, func(client *Redis) { - err := New(client.Addr, badType()).Setex("hello", "world", 5) + err := newRedis(client.Addr, badType()).Setex("hello", "world", 5) assert.NotNil(t, err) err = client.Setex("hello", "world", 5) assert.Nil(t, err) - _, err = New(client.Addr, badType()).Setnx("hello", "newworld") + _, err = newRedis(client.Addr, badType()).Setnx("hello", "newworld") assert.NotNil(t, err) ok, err := client.Setnx("hello", "newworld") assert.Nil(t, err) @@ -1104,7 +1149,7 @@ func TestRedis_SetExNx(t *testing.T) { ttl, err := client.Ttl("hello") assert.Nil(t, err) assert.True(t, ttl > 0) - _, err = New(client.Addr, badType()).SetnxEx("newhello", "newworld", 5) + _, err = newRedis(client.Addr, badType()).SetnxEx("newhello", "newworld", 5) assert.NotNil(t, err) ok, err = client.SetnxEx("newhello", "newworld", 5) assert.Nil(t, err) @@ -1126,17 +1171,17 @@ func TestRedis_SetGetDelHashField(t *testing.T) { runOnRedis(t, func(client *Redis) { err := client.Hset("key", "field", "value") assert.Nil(t, err) - _, err = New(client.Addr, badType()).Hget("key", "field") + _, err = newRedis(client.Addr, badType()).Hget("key", "field") assert.NotNil(t, err) val, err := client.Hget("key", "field") assert.Nil(t, err) assert.Equal(t, "value", val) - _, err = New(client.Addr, badType()).Hexists("key", "field") + _, err = newRedis(client.Addr, badType()).Hexists("key", "field") assert.NotNil(t, err) ok, err := client.Hexists("key", "field") assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Hdel("key", "field") + _, err = newRedis(client.Addr, badType()).Hdel("key", "field") assert.NotNil(t, err) ret, err := client.Hdel("key", "field") assert.Nil(t, err) @@ -1179,17 +1224,17 @@ func TestRedis_SortedSet(t *testing.T) { val, err := client.Zscore("key", "value1") assert.Nil(t, err) assert.Equal(t, int64(2), val) - _, err = New(client.Addr, badType()).Zincrby("key", 3, "value1") + _, err = newRedis(client.Addr, badType()).Zincrby("key", 3, "value1") assert.NotNil(t, err) val, err = client.Zincrby("key", 3, "value1") assert.Nil(t, err) assert.Equal(t, int64(5), val) - _, err = New(client.Addr, badType()).Zscore("key", "value1") + _, err = newRedis(client.Addr, badType()).Zscore("key", "value1") assert.NotNil(t, err) val, err = client.Zscore("key", "value1") assert.Nil(t, err) assert.Equal(t, int64(5), val) - _, err = New(client.Addr, badType()).Zadds("key") + _, err = newRedis(client.Addr, badType()).Zadds("key") assert.NotNil(t, err) val, err = client.Zadds("key", Pair{ Key: "value2", @@ -1200,7 +1245,7 @@ func TestRedis_SortedSet(t *testing.T) { }) assert.Nil(t, err) assert.Equal(t, int64(2), val) - _, err = New(client.Addr, badType()).ZRevRangeWithScores("key", 1, 3) + _, err = newRedis(client.Addr, badType()).ZRevRangeWithScores("key", 1, 3) assert.NotNil(t, err) _, err = client.ZRevRangeWithScores("key", 1, 3) assert.Nil(t, err) @@ -1224,11 +1269,11 @@ func TestRedis_SortedSet(t *testing.T) { rank, err = client.Zrevrank("key", "value1") assert.Nil(t, err) assert.Equal(t, int64(2), rank) - _, err = New(client.Addr, badType()).Zrank("key", "value4") + _, err = newRedis(client.Addr, badType()).Zrank("key", "value4") assert.NotNil(t, err) _, err = client.Zrank("key", "value4") assert.Equal(t, Nil, err) - _, err = New(client.Addr, badType()).Zrem("key", "value2", "value3") + _, err = newRedis(client.Addr, badType()).Zrem("key", "value2", "value3") assert.NotNil(t, err) num, err := client.Zrem("key", "value2", "value3") assert.Nil(t, err) @@ -1242,7 +1287,7 @@ func TestRedis_SortedSet(t *testing.T) { ok, err = client.Zadd("key", 8, "value4") assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Zremrangebyscore("key", 6, 7) + _, err = newRedis(client.Addr, badType()).Zremrangebyscore("key", 6, 7) assert.NotNil(t, err) num, err = client.Zremrangebyscore("key", 6, 7) assert.Nil(t, err) @@ -1250,37 +1295,37 @@ func TestRedis_SortedSet(t *testing.T) { ok, err = client.Zadd("key", 6, "value2") assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Zadd("key", 7, "value3") + _, err = newRedis(client.Addr, badType()).Zadd("key", 7, "value3") assert.NotNil(t, err) ok, err = client.Zadd("key", 7, "value3") assert.Nil(t, err) assert.True(t, ok) - _, err = New(client.Addr, badType()).Zcount("key", 6, 7) + _, err = newRedis(client.Addr, badType()).Zcount("key", 6, 7) assert.NotNil(t, err) num, err = client.Zcount("key", 6, 7) assert.Nil(t, err) assert.Equal(t, 2, num) - _, err = New(client.Addr, badType()).Zremrangebyrank("key", 1, 2) + _, err = newRedis(client.Addr, badType()).Zremrangebyrank("key", 1, 2) assert.NotNil(t, err) num, err = client.Zremrangebyrank("key", 1, 2) assert.Nil(t, err) assert.Equal(t, 2, num) - _, err = New(client.Addr, badType()).Zcard("key") + _, err = newRedis(client.Addr, badType()).Zcard("key") assert.NotNil(t, err) card, err := client.Zcard("key") assert.Nil(t, err) assert.Equal(t, 2, card) - _, err = New(client.Addr, badType()).Zrange("key", 0, -1) + _, err = newRedis(client.Addr, badType()).Zrange("key", 0, -1) assert.NotNil(t, err) vals, err := client.Zrange("key", 0, -1) assert.Nil(t, err) assert.EqualValues(t, []string{"value1", "value4"}, vals) - _, err = New(client.Addr, badType()).Zrevrange("key", 0, -1) + _, err = newRedis(client.Addr, badType()).Zrevrange("key", 0, -1) assert.NotNil(t, err) vals, err = client.Zrevrange("key", 0, -1) assert.Nil(t, err) assert.EqualValues(t, []string{"value4", "value1"}, vals) - _, err = New(client.Addr, badType()).ZrangeWithScores("key", 0, -1) + _, err = newRedis(client.Addr, badType()).ZrangeWithScores("key", 0, -1) assert.NotNil(t, err) pairs, err = client.ZrangeWithScores("key", 0, -1) assert.Nil(t, err) @@ -1294,7 +1339,7 @@ func TestRedis_SortedSet(t *testing.T) { Score: 8, }, }, pairs) - _, err = New(client.Addr, badType()).ZrangebyscoreWithScores("key", 5, 8) + _, err = newRedis(client.Addr, badType()).ZrangebyscoreWithScores("key", 5, 8) assert.NotNil(t, err) pairs, err = client.ZrangebyscoreWithScores("key", 5, 8) assert.Nil(t, err) @@ -1308,7 +1353,7 @@ func TestRedis_SortedSet(t *testing.T) { Score: 8, }, }, pairs) - _, err = New(client.Addr, badType()).ZrangebyscoreWithScoresAndLimit( + _, err = newRedis(client.Addr, badType()).ZrangebyscoreWithScoresAndLimit( "key", 5, 8, 1, 1) assert.NotNil(t, err) pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1) @@ -1322,7 +1367,7 @@ func TestRedis_SortedSet(t *testing.T) { pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 0) assert.Nil(t, err) assert.Equal(t, 0, len(pairs)) - _, err = New(client.Addr, badType()).ZrevrangebyscoreWithScores("key", 5, 8) + _, err = newRedis(client.Addr, badType()).ZrevrangebyscoreWithScores("key", 5, 8) assert.NotNil(t, err) pairs, err = client.ZrevrangebyscoreWithScores("key", 5, 8) assert.Nil(t, err) @@ -1336,7 +1381,7 @@ func TestRedis_SortedSet(t *testing.T) { Score: 5, }, }, pairs) - _, err = New(client.Addr, badType()).ZrevrangebyscoreWithScoresAndLimit( + _, err = newRedis(client.Addr, badType()).ZrevrangebyscoreWithScoresAndLimit( "key", 5, 8, 1, 1) assert.NotNil(t, err) pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1) @@ -1350,7 +1395,7 @@ func TestRedis_SortedSet(t *testing.T) { pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 0) assert.Nil(t, err) assert.Equal(t, 0, len(pairs)) - _, err = New(client.Addr, badType()).Zrevrank("key", "value") + _, err = newRedis(client.Addr, badType()).Zrevrank("key", "value") assert.NotNil(t, err) _, _ = client.Zadd("second", 2, "aa") _, _ = client.Zadd("third", 3, "bbb") @@ -1361,7 +1406,7 @@ func TestRedis_SortedSet(t *testing.T) { }) assert.Nil(t, err) assert.Equal(t, int64(2), val) - _, err = New(client.Addr, badType()).Zunionstore("union", &ZStore{}) + _, err = newRedis(client.Addr, badType()).Zunionstore("union", &ZStore{}) assert.NotNil(t, err) vals, err = client.Zrange("union", 0, 10000) assert.Nil(t, err) @@ -1488,10 +1533,10 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { val, err := client.ZscoreByFloat("key", "value1") assert.Nil(t, err) assert.Equal(t, 10.345, val) - _, err = New(client.Addr, badType()).ZscoreByFloat("key", "value1") + _, err = newRedis(client.Addr, badType()).ZscoreByFloat("key", "value1") assert.Error(t, err) _, _ = client.ZaddFloat("key", 10.346, "value2") - _, err = New(client.Addr, badType()).ZRevRangeWithScoresByFloat("key", 0, -1) + _, err = newRedis(client.Addr, badType()).ZRevRangeWithScoresByFloat("key", 0, -1) assert.NotNil(t, err) _, err = client.ZRevRangeWithScoresByFloat("key", 0, -1) assert.Nil(t, err) @@ -1510,7 +1555,7 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { }, }, pairs) - _, err = New(client.Addr, badType()).ZrangeWithScoresByFloat("key", 0, -1) + _, err = newRedis(client.Addr, badType()).ZrangeWithScoresByFloat("key", 0, -1) assert.Error(t, err) pairs, err = client.ZrangeWithScoresByFloat("key", 0, -1) @@ -1527,7 +1572,7 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { }, pairs) } - _, err = New(client.Addr, badType()).ZrangebyscoreWithScoresByFloat("key", 0, 20) + _, err = newRedis(client.Addr, badType()).ZrangebyscoreWithScoresByFloat("key", 0, 20) assert.NotNil(t, err) pairs, err = client.ZrangebyscoreWithScoresByFloat("key", 0, 20) assert.Nil(t, err) @@ -1543,7 +1588,7 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { }, pairs) _, err = client.ZrangebyscoreWithScoresByFloatAndLimit("key", 10.1, 12.2, 1, 0) assert.NoError(t, err) - _, err = New(client.Addr, badType()).ZrangebyscoreWithScoresByFloatAndLimit( + _, err = newRedis(client.Addr, badType()).ZrangebyscoreWithScoresByFloatAndLimit( "key", 10.1, 12.2, 1, 1) assert.NotNil(t, err) pairs, err = client.ZrangebyscoreWithScoresByFloatAndLimit("key", 10.1, 12.2, 1, 1) @@ -1554,7 +1599,7 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { Score: 10.346, }, }, pairs) - _, err = New(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloat("key", 10, 12) + _, err = newRedis(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloat("key", 10, 12) assert.NotNil(t, err) pairs, err = client.ZrevrangebyscoreWithScoresByFloat("key", 10, 12) assert.Nil(t, err) @@ -1570,7 +1615,7 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { }, pairs) _, err = client.ZrevrangebyscoreWithScoresByFloatAndLimit("key", 10, 12, 1, 0) assert.NoError(t, err) - _, err = New(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloatAndLimit( + _, err = newRedis(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloatAndLimit( "key", 10, 12, 1, 1) assert.NotNil(t, err) pairs, err = client.ZrevrangebyscoreWithScoresByFloatAndLimit("key", 10, 12, 1, 1) @@ -1584,17 +1629,43 @@ func TestRedis_SortedSetByFloat64(t *testing.T) { }) } +func TestRedis_Zaddnx(t *testing.T) { + runOnRedis(t, func(client *Redis) { + _, err := newRedis(client.Addr, badType()).Zaddnx("key", 1, "value1") + assert.Error(t, err) + + ok, err := client.Zadd("key", 1, "value1") + assert.NoError(t, err) + assert.True(t, ok) + ok, err = client.Zaddnx("key", 2, "value1") + assert.NoError(t, err) + assert.False(t, ok) + + ok, err = client.ZaddFloat("key", 1.1, "value2") + assert.NoError(t, err) + assert.True(t, ok) + ok, err = client.ZaddnxFloat("key", 1.1, "value3") + assert.NoError(t, err) + assert.True(t, ok) + + assert.NoError(t, client.Set("newkey", "value")) + ok, err = client.Zaddnx("newkey", 1, "value") + assert.Error(t, err) + assert.False(t, ok) + }) +} + func TestRedis_IncrbyFloat(t *testing.T) { runOnRedis(t, func(client *Redis) { incrVal, err := client.IncrbyFloat("key", 0.002) assert.Nil(t, err) assert.Equal(t, 0.002, incrVal) - _, err = New(client.Addr, badType()).IncrbyFloat("key", -0.001) + _, err = newRedis(client.Addr, badType()).IncrbyFloat("key", -0.001) assert.Error(t, err) incrVal2, err := client.IncrbyFloat("key", -0.001) assert.Nil(t, err) assert.Equal(t, 0.001, incrVal2) - _, err = New(client.Addr, badType()).HincrbyFloat("hkey", "i", 0.002) + _, err = newRedis(client.Addr, badType()).HincrbyFloat("hkey", "i", 0.002) assert.Error(t, err) hincrVal, err := client.HincrbyFloat("hkey", "i", 0.002) assert.Nil(t, err) @@ -1607,7 +1678,7 @@ func TestRedis_IncrbyFloat(t *testing.T) { func TestRedis_Pipelined(t *testing.T) { runOnRedis(t, func(client *Redis) { - assert.NotNil(t, New(client.Addr, badType()).Pipelined(func(pipeliner Pipeliner) error { + assert.NotNil(t, newRedis(client.Addr, badType()).Pipelined(func(pipeliner Pipeliner) error { return nil })) err := client.Pipelined( @@ -1619,7 +1690,7 @@ func TestRedis_Pipelined(t *testing.T) { }, ) assert.Nil(t, err) - _, err = New(client.Addr, badType()).Ttl("pipelined_counter") + _, err = newRedis(client.Addr, badType()).Ttl("pipelined_counter") assert.NotNil(t, err) ttl, err := client.Ttl("pipelined_counter") assert.Nil(t, err) @@ -1636,17 +1707,17 @@ func TestRedis_Pipelined(t *testing.T) { func TestRedisString(t *testing.T) { runOnRedis(t, func(client *Redis) { client.Ping() - _, err := getRedis(New(client.Addr, Cluster())) + _, err := getRedis(newRedis(client.Addr, Cluster())) assert.Nil(t, err) assert.Equal(t, client.Addr, client.String()) - assert.NotNil(t, New(client.Addr, badType()).Ping()) + assert.NotNil(t, newRedis(client.Addr, badType()).Ping()) }) } func TestRedisScriptLoad(t *testing.T) { runOnRedis(t, func(client *Redis) { client.Ping() - _, err := New(client.Addr, badType()).ScriptLoad("foo") + _, err := newRedis(client.Addr, badType()).ScriptLoad("foo") assert.NotNil(t, err) _, err = client.ScriptLoad("foo") assert.NotNil(t, err) @@ -1658,7 +1729,7 @@ func TestRedisEvalSha(t *testing.T) { client.Ping() scriptHash, err := client.ScriptLoad(`return redis.call("EXISTS", KEYS[1])`) assert.Nil(t, err) - _, err = New(client.Addr, badType()).EvalSha(scriptHash, []string{"key1"}) + _, err = newRedis(client.Addr, badType()).EvalSha(scriptHash, []string{"key1"}) assert.Error(t, err) result, err := client.EvalSha(scriptHash, []string{"key1"}) assert.Nil(t, err) @@ -1741,7 +1812,7 @@ func TestRedis_Zscan(t *testing.T) { var cursor uint64 = 0 sum := 0 for { - _, _, err := New(client.Addr, badType()).Zscan(key, cursor, "value_*", 100) + _, _, err := newRedis(client.Addr, badType()).Zscan(key, cursor, "value_*", 100) assert.NotNil(t, err) keys, next, err := client.Zscan(key, cursor, "value_*", 100) assert.Nil(t, err) @@ -1753,7 +1824,7 @@ func TestRedis_Zscan(t *testing.T) { } assert.Equal(t, sum, 3100) - _, err := New(client.Addr, badType()).Del(key) + _, err := newRedis(client.Addr, badType()).Del(key) assert.NotNil(t, err) _, err = client.Del(key) assert.Nil(t, err) @@ -1770,9 +1841,9 @@ func TestRedisBlpop(t *testing.T) { client.Ping() var node mockedNode _, err := client.Blpop(nil, "foo") - assert.NotNil(t, err) + assert.Error(t, err) _, err = client.Blpop(node, "foo") - assert.NotNil(t, err) + assert.NoError(t, err) }) } @@ -1784,19 +1855,60 @@ func TestRedisBlpopEx(t *testing.T) { _, _, err := client.BlpopEx(nil, "foo") assert.Error(t, err) _, _, err = client.BlpopEx(node, "foo") + assert.NoError(t, err) + }) + }) + + t.Run("blpopex with expire", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + client.Ping() + node, err := getRedis(client) + assert.NoError(t, err) + assert.NoError(t, client.Set("foo", "bar")) + _, _, err = client.BlpopEx(node, "foo") + assert.Error(t, err) + }) + }) + + t.Run("blpopex with bad return", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + client.Ping() + node := &mockedNode{args: []string{"bar"}} + _, _, err := client.BlpopEx(node, "foo") assert.Error(t, err) }) }) } func TestRedisBlpopWithTimeout(t *testing.T) { - runOnRedis(t, func(client *Redis) { - client.Ping() - var node mockedNode - _, err := client.BlpopWithTimeout(nil, 10*time.Second, "foo") - assert.NotNil(t, err) - _, err = client.BlpopWithTimeout(node, 10*time.Second, "foo") - assert.NotNil(t, err) + t.Run("blpop_withTimeout", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + client.Ping() + var node mockedNode + _, err := client.BlpopWithTimeout(nil, 10*time.Second, "foo") + assert.Error(t, err) + _, err = client.BlpopWithTimeout(node, 10*time.Second, "foo") + assert.NoError(t, err) + }) + }) + + t.Run("blpop_withTimeout_error", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + client.Ping() + node, err := getRedis(client) + assert.NoError(t, err) + assert.NoError(t, client.Set("foo", "bar")) + _, err = client.BlpopWithTimeout(node, time.Millisecond, "foo") + assert.Error(t, err) + }) + }) + + t.Run("blpop_with_bad_return", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + node := &mockedNode{args: []string{"foo"}} + _, err := client.Blpop(node, "foo") + assert.Error(t, err) + }) }) } @@ -1809,13 +1921,13 @@ func TestRedisGeo(t *testing.T) { v, err := client.GeoAdd("sicily", geoLocation...) assert.Nil(t, err) assert.Equal(t, int64(2), v) - _, err = New(client.Addr, badType()).GeoDist("sicily", "Palermo", "Catania", "m") + _, err = newRedis(client.Addr, badType()).GeoDist("sicily", "Palermo", "Catania", "m") assert.Error(t, err) v2, err := client.GeoDist("sicily", "Palermo", "Catania", "m") assert.Nil(t, err) assert.Equal(t, 166274, int(v2)) // GeoHash not support - _, err = New(client.Addr, badType()).GeoPos("sicily", "Palermo", "Catania") + _, err = newRedis(client.Addr, badType()).GeoPos("sicily", "Palermo", "Catania") assert.Error(t, err) v3, err := client.GeoPos("sicily", "Palermo", "Catania") assert.Nil(t, err) @@ -1823,7 +1935,7 @@ func TestRedisGeo(t *testing.T) { assert.Equal(t, int64(v3[0].Latitude), int64(38)) assert.Equal(t, int64(v3[1].Longitude), int64(15)) assert.Equal(t, int64(v3[1].Latitude), int64(37)) - _, err = New(client.Addr, badType()).GeoRadius("sicily", 15, 37, + _, err = newRedis(client.Addr, badType()).GeoRadius("sicily", 15, 37, &red.GeoRadiusQuery{WithDist: true, Unit: "km", Radius: 200}) assert.Error(t, err) v4, err := client.GeoRadius("sicily", 15, 37, &red.GeoRadiusQuery{ @@ -1834,12 +1946,12 @@ func TestRedisGeo(t *testing.T) { assert.Equal(t, int64(v4[0].Dist), int64(190)) assert.Equal(t, int64(v4[1].Dist), int64(56)) geoLocation2 := []*GeoLocation{{Longitude: 13.583333, Latitude: 37.316667, Name: "Agrigento"}} - _, err = New(client.Addr, badType()).GeoAdd("sicily", geoLocation2...) + _, err = newRedis(client.Addr, badType()).GeoAdd("sicily", geoLocation2...) assert.Error(t, err) v5, err := client.GeoAdd("sicily", geoLocation2...) assert.Nil(t, err) assert.Equal(t, int64(1), v5) - _, err = New(client.Addr, badType()).GeoRadiusByMember("sicily", "Agrigento", + _, err = newRedis(client.Addr, badType()).GeoRadiusByMember("sicily", "Agrigento", &red.GeoRadiusQuery{Unit: "km", Radius: 100}) assert.Error(t, err) v6, err := client.GeoRadiusByMember("sicily", "Agrigento", @@ -1886,11 +1998,27 @@ func TestSetSlowThreshold(t *testing.T) { func TestRedis_WithPass(t *testing.T) { runOnRedis(t, func(client *Redis) { - err := New(client.Addr, WithPass("any")).Ping() + err := newRedis(client.Addr, WithPass("any")).Ping() assert.NotNil(t, err) }) } +func TestRedis_checkConnection(t *testing.T) { + t.Run("checkConnection", func(t *testing.T) { + runOnRedis(t, func(client *Redis) { + client.Ping() + assert.NoError(t, client.checkConnection(time.Millisecond)) + }) + }) + + t.Run("checkConnection error", func(t *testing.T) { + runOnRedisWithError(t, func(client *Redis) { + assert.Error(t, newRedis(client.Addr, badType()).checkConnection(time.Millisecond)) + assert.Error(t, client.checkConnection(time.Millisecond)) + }) + }) +} + func runOnRedis(t *testing.T, fn func(client *Redis)) { logx.Disable() @@ -1906,7 +2034,7 @@ func runOnRedisWithError(t *testing.T, fn func(client *Redis)) { s := miniredis.RunT(t) s.SetError("mock error") - fn(New(s.Addr())) + fn(newRedis(s.Addr())) } func runOnRedisTLS(t *testing.T, fn func(client *Redis)) { @@ -1928,7 +2056,7 @@ func runOnRedisTLS(t *testing.T, fn func(client *Redis)) { _ = client.Close() } }() - fn(New(s.Addr(), WithTLS())) + fn(newRedis(s.Addr(), WithTLS())) } func badType() Option { @@ -1939,8 +2067,16 @@ func badType() Option { type mockedNode struct { RedisNode + args []string } func (n mockedNode) BLPop(_ context.Context, _ time.Duration, _ ...string) *red.StringSliceCmd { - return red.NewStringSliceCmd(context.Background(), "foo", "bar") + cmd := red.NewStringSliceCmd(context.Background()) + if len(n.args) == 0 { + cmd.SetVal([]string{"foo", "bar"}) + } else { + cmd.SetVal(n.args) + } + + return cmd } diff --git a/core/stores/redis/redisclientmanager.go b/core/stores/redis/redisclientmanager.go index e2e1ad5f..14e60871 100644 --- a/core/stores/redis/redisclientmanager.go +++ b/core/stores/redis/redisclientmanager.go @@ -37,8 +37,11 @@ func getClient(r *Redis) (*red.Client, error) { MinIdleConns: idleConns, TLSConfig: tlsConfig, }) - store.AddHook(durationHook) - for _, hook := range r.hooks { + + hooks := append([]red.Hook{defaultDurationHook, breakerHook{ + brk: r.brk, + }}, r.hooks...) + for _, hook := range hooks { store.AddHook(hook) } diff --git a/core/stores/redis/redisclustermanager.go b/core/stores/redis/redisclustermanager.go index e361a577..5013de83 100644 --- a/core/stores/redis/redisclustermanager.go +++ b/core/stores/redis/redisclustermanager.go @@ -33,8 +33,11 @@ func getCluster(r *Redis) (*red.ClusterClient, error) { MinIdleConns: idleConns, TLSConfig: tlsConfig, }) - store.AddHook(durationHook) - for _, hook := range r.hooks { + + hooks := append([]red.Hook{defaultDurationHook, breakerHook{ + brk: r.brk, + }}, r.hooks...) + for _, hook := range hooks { store.AddHook(hook) } diff --git a/core/stores/redis/redisclustermanager_test.go b/core/stores/redis/redisclustermanager_test.go index ac31517e..ad418558 100644 --- a/core/stores/redis/redisclustermanager_test.go +++ b/core/stores/redis/redisclustermanager_test.go @@ -51,7 +51,7 @@ func TestGetCluster(t *testing.T) { Addr: r.Addr(), Type: ClusterType, tls: true, - hooks: []red.Hook{durationHook}, + hooks: []red.Hook{defaultDurationHook}, }) if assert.NoError(t, err) { assert.NotNil(t, c) diff --git a/core/stores/redis/redislock.go b/core/stores/redis/redislock.go index 2979ee50..ab26cc98 100644 --- a/core/stores/redis/redislock.go +++ b/core/stores/redis/redislock.go @@ -2,6 +2,7 @@ package redis import ( "context" + "errors" "math/rand" "strconv" "sync/atomic" @@ -41,7 +42,7 @@ type RedisLock struct { } func init() { - rand.Seed(time.Now().UnixNano()) + rand.NewSource(time.Now().UnixNano()) } // NewRedisLock returns a RedisLock. @@ -64,7 +65,7 @@ func (rl *RedisLock) AcquireCtx(ctx context.Context) (bool, error) { resp, err := rl.store.ScriptRunCtx(ctx, lockScript, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance), }) - if err == red.Nil { + if errors.Is(err, red.Nil) { return false, nil } else if err != nil { logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error()) diff --git a/core/stores/sqlx/mysql.go b/core/stores/sqlx/mysql.go index 3c026921..e7797095 100644 --- a/core/stores/sqlx/mysql.go +++ b/core/stores/sqlx/mysql.go @@ -13,7 +13,7 @@ const ( // NewMysql returns a mysql connection. func NewMysql(datasource string, opts ...SqlOption) SqlConn { - opts = append(opts, withMysqlAcceptable()) + opts = append([]SqlOption{withMysqlAcceptable()}, opts...) return NewSqlConn(mysqlDriverName, datasource, opts...) } diff --git a/core/stores/sqlx/mysql_test.go b/core/stores/sqlx/mysql_test.go index 68698e4b..1bf33630 100644 --- a/core/stores/sqlx/mysql_test.go +++ b/core/stores/sqlx/mysql_test.go @@ -2,7 +2,6 @@ package sqlx import ( "errors" - "reflect" "testing" "github.com/go-sql-driver/mysql" @@ -38,7 +37,6 @@ func TestBreakerOnNotHandlingDuplicateEntry(t *testing.T) { func TestMysqlAcceptable(t *testing.T) { conn := NewMysql("nomysql").(*commonSqlConn) withMysqlAcceptable()(conn) - assert.EqualValues(t, reflect.ValueOf(mysqlAcceptable).Pointer(), reflect.ValueOf(conn.accept).Pointer()) assert.True(t, mysqlAcceptable(nil)) assert.False(t, mysqlAcceptable(errors.New("any"))) assert.False(t, mysqlAcceptable(new(mysql.MySQLError))) diff --git a/core/stores/sqlx/sqlconn.go b/core/stores/sqlx/sqlconn.go index 9603af5b..d6ca746d 100644 --- a/core/stores/sqlx/sqlconn.go +++ b/core/stores/sqlx/sqlconn.go @@ -42,21 +42,6 @@ type ( // SqlOption defines the method to customize a sql connection. SqlOption func(*commonSqlConn) - // StmtSession interface represents a session that can be used to execute statements. - StmtSession interface { - Close() error - Exec(args ...any) (sql.Result, error) - ExecCtx(ctx context.Context, args ...any) (sql.Result, error) - QueryRow(v any, args ...any) error - QueryRowCtx(ctx context.Context, v any, args ...any) error - QueryRowPartial(v any, args ...any) error - QueryRowPartialCtx(ctx context.Context, v any, args ...any) error - QueryRows(v any, args ...any) error - QueryRowsCtx(ctx context.Context, v any, args ...any) error - QueryRowsPartial(v any, args ...any) error - QueryRowsPartialCtx(ctx context.Context, v any, args ...any) error - } - // thread-safe // Because CORBA doesn't support PREPARE, so we need to combine the // query arguments into one string and do underlying query without arguments @@ -65,7 +50,7 @@ type ( onError func(context.Context, error) beginTx beginnable brk breaker.Breaker - accept func(error) bool + accept breaker.Acceptable } connProvider func() (*sql.DB, error) @@ -76,18 +61,6 @@ type ( Query(query string, args ...any) (*sql.Rows, error) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) } - - statement struct { - query string - stmt *sql.Stmt - } - - stmtConn interface { - Exec(args ...any) (sql.Result, error) - ExecContext(ctx context.Context, args ...any) (sql.Result, error) - Query(args ...any) (*sql.Rows, error) - QueryContext(ctx context.Context, args ...any) (*sql.Rows, error) - } ) // NewSqlConn returns a SqlConn with given driver name and datasource. @@ -189,8 +162,10 @@ func (db *commonSqlConn) PrepareCtx(ctx context.Context, query string) (stmt Stm } stmt = statement{ - query: query, - stmt: st, + query: query, + stmt: st, + brk: db.brk, + accept: db.acceptable, } return nil }, db.acceptable) @@ -311,7 +286,7 @@ func (db *commonSqlConn) acceptable(err error) bool { func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) error, q string, args ...any) (err error) { - var qerr error + var scanFailed bool err = db.brk.DoWithAcceptable(func() error { conn, err := db.connProv() if err != nil { @@ -320,11 +295,14 @@ func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) } return query(ctx, conn, func(rows *sql.Rows) error { - qerr = scanner(rows) - return qerr + e := scanner(rows) + if e != nil { + scanFailed = true + } + return e }, q, args...) }, func(err error) bool { - return errors.Is(err, qerr) || db.acceptable(err) + return scanFailed || db.acceptable(err) }) if errors.Is(err, breaker.ErrServiceUnavailable) { metricReqErr.Inc("queryRows", "breaker") @@ -333,87 +311,17 @@ func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) return } -func (s statement) Close() error { - return s.stmt.Close() -} - -func (s statement) Exec(args ...any) (sql.Result, error) { - return s.ExecCtx(context.Background(), args...) -} - -func (s statement) ExecCtx(ctx context.Context, args ...any) (result sql.Result, err error) { - ctx, span := startSpan(ctx, "Exec") - defer func() { - endSpan(span, err) - }() - - return execStmt(ctx, s.stmt, s.query, args...) -} - -func (s statement) QueryRow(v any, args ...any) error { - return s.QueryRowCtx(context.Background(), v, args...) -} - -func (s statement) QueryRowCtx(ctx context.Context, v any, args ...any) (err error) { - ctx, span := startSpan(ctx, "QueryRow") - defer func() { - endSpan(span, err) - }() - - return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { - return unmarshalRow(v, rows, true) - }, s.query, args...) -} - -func (s statement) QueryRowPartial(v any, args ...any) error { - return s.QueryRowPartialCtx(context.Background(), v, args...) -} - -func (s statement) QueryRowPartialCtx(ctx context.Context, v any, args ...any) (err error) { - ctx, span := startSpan(ctx, "QueryRowPartial") - defer func() { - endSpan(span, err) - }() - - return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { - return unmarshalRow(v, rows, false) - }, s.query, args...) -} - -func (s statement) QueryRows(v any, args ...any) error { - return s.QueryRowsCtx(context.Background(), v, args...) -} - -func (s statement) QueryRowsCtx(ctx context.Context, v any, args ...any) (err error) { - ctx, span := startSpan(ctx, "QueryRows") - defer func() { - endSpan(span, err) - }() - - return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { - return unmarshalRows(v, rows, true) - }, s.query, args...) -} - -func (s statement) QueryRowsPartial(v any, args ...any) error { - return s.QueryRowsPartialCtx(context.Background(), v, args...) -} - -func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any) (err error) { - ctx, span := startSpan(ctx, "QueryRowsPartial") - defer func() { - endSpan(span, err) - }() - - return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { - return unmarshalRows(v, rows, false) - }, s.query, args...) -} - // WithAcceptable returns a SqlOption that setting the acceptable function. // acceptable is the func to check if the error can be accepted. func WithAcceptable(acceptable func(err error) bool) SqlOption { return func(conn *commonSqlConn) { - conn.accept = acceptable + if conn.accept == nil { + conn.accept = acceptable + } else { + pre := conn.accept + conn.accept = func(err error) bool { + return pre(err) || acceptable(err) + } + } } } diff --git a/core/stores/sqlx/sqlconn_test.go b/core/stores/sqlx/sqlconn_test.go index 339d30cc..53ea0d5a 100644 --- a/core/stores/sqlx/sqlconn_test.go +++ b/core/stores/sqlx/sqlconn_test.go @@ -156,6 +156,7 @@ func TestStatement(t *testing.T) { st := statement{ query: "foo", stmt: stmt, + brk: breaker.NopBreaker(), } assert.NoError(t, st.Close()) }) @@ -263,6 +264,45 @@ func TestBreakerWithScanError(t *testing.T) { }) } +func TestWithAcceptable(t *testing.T) { + var ( + acceptableErr = errors.New("acceptable") + acceptableErr2 = errors.New("acceptable2") + acceptableErr3 = errors.New("acceptable3") + ) + opts := []SqlOption{ + WithAcceptable(func(err error) bool { + if err == nil { + return true + } + return errors.Is(err, acceptableErr) + }), + WithAcceptable(func(err error) bool { + if err == nil { + return true + } + return errors.Is(err, acceptableErr2) + }), + WithAcceptable(func(err error) bool { + if err == nil { + return true + } + return errors.Is(err, acceptableErr3) + }), + } + + var conn = &commonSqlConn{} + for _, opt := range opts { + opt(conn) + } + + assert.True(t, conn.accept(nil)) + assert.False(t, conn.accept(assert.AnError)) + assert.True(t, conn.accept(acceptableErr)) + assert.True(t, conn.accept(acceptableErr2)) + assert.True(t, conn.accept(acceptableErr3)) +} + func buildConn() (mock sqlmock.Sqlmock, err error) { _, err = connManager.GetResource(mockedDatasource, func() (io.Closer, error) { var db *sql.DB diff --git a/core/stores/sqlx/stmt.go b/core/stores/sqlx/stmt.go index ab688abb..b66dcc7f 100644 --- a/core/stores/sqlx/stmt.go +++ b/core/stores/sqlx/stmt.go @@ -3,8 +3,10 @@ package sqlx import ( "context" "database/sql" + "errors" "time" + "github.com/zeromicro/go-zero/core/breaker" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/syncx" "github.com/zeromicro/go-zero/core/timex" @@ -18,6 +20,145 @@ var ( logSlowSql = syncx.ForAtomicBool(true) ) +type ( + // StmtSession interface represents a session that can be used to execute statements. + StmtSession interface { + Close() error + Exec(args ...any) (sql.Result, error) + ExecCtx(ctx context.Context, args ...any) (sql.Result, error) + QueryRow(v any, args ...any) error + QueryRowCtx(ctx context.Context, v any, args ...any) error + QueryRowPartial(v any, args ...any) error + QueryRowPartialCtx(ctx context.Context, v any, args ...any) error + QueryRows(v any, args ...any) error + QueryRowsCtx(ctx context.Context, v any, args ...any) error + QueryRowsPartial(v any, args ...any) error + QueryRowsPartialCtx(ctx context.Context, v any, args ...any) error + } + + statement struct { + query string + stmt *sql.Stmt + brk breaker.Breaker + accept breaker.Acceptable + } + + stmtConn interface { + Exec(args ...any) (sql.Result, error) + ExecContext(ctx context.Context, args ...any) (sql.Result, error) + Query(args ...any) (*sql.Rows, error) + QueryContext(ctx context.Context, args ...any) (*sql.Rows, error) + } +) + +func (s statement) Close() error { + return s.stmt.Close() +} + +func (s statement) Exec(args ...any) (sql.Result, error) { + return s.ExecCtx(context.Background(), args...) +} + +func (s statement) ExecCtx(ctx context.Context, args ...any) (result sql.Result, err error) { + ctx, span := startSpan(ctx, "Exec") + defer func() { + endSpan(span, err) + }() + + err = s.brk.DoWithAcceptable(func() error { + result, err = execStmt(ctx, s.stmt, s.query, args...) + return err + }, func(err error) bool { + return s.accept(err) + }) + if errors.Is(err, breaker.ErrServiceUnavailable) { + metricReqErr.Inc("stmt_exec", "breaker") + } + + return +} + +func (s statement) QueryRow(v any, args ...any) error { + return s.QueryRowCtx(context.Background(), v, args...) +} + +func (s statement) QueryRowCtx(ctx context.Context, v any, args ...any) (err error) { + ctx, span := startSpan(ctx, "QueryRow") + defer func() { + endSpan(span, err) + }() + + return s.queryRows(ctx, func(v any, scanner rowsScanner) error { + return unmarshalRow(v, scanner, true) + }, v, args...) +} + +func (s statement) QueryRowPartial(v any, args ...any) error { + return s.QueryRowPartialCtx(context.Background(), v, args...) +} + +func (s statement) QueryRowPartialCtx(ctx context.Context, v any, args ...any) (err error) { + ctx, span := startSpan(ctx, "QueryRowPartial") + defer func() { + endSpan(span, err) + }() + + return s.queryRows(ctx, func(v any, scanner rowsScanner) error { + return unmarshalRow(v, scanner, false) + }, v, args...) +} + +func (s statement) QueryRows(v any, args ...any) error { + return s.QueryRowsCtx(context.Background(), v, args...) +} + +func (s statement) QueryRowsCtx(ctx context.Context, v any, args ...any) (err error) { + ctx, span := startSpan(ctx, "QueryRows") + defer func() { + endSpan(span, err) + }() + + return s.queryRows(ctx, func(v any, scanner rowsScanner) error { + return unmarshalRows(v, scanner, true) + }, v, args...) +} + +func (s statement) QueryRowsPartial(v any, args ...any) error { + return s.QueryRowsPartialCtx(context.Background(), v, args...) +} + +func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any) (err error) { + ctx, span := startSpan(ctx, "QueryRowsPartial") + defer func() { + endSpan(span, err) + }() + + return s.queryRows(ctx, func(v any, scanner rowsScanner) error { + return unmarshalRows(v, scanner, false) + }, v, args...) +} + +func (s statement) queryRows(ctx context.Context, scanFn func(any, rowsScanner) error, + v any, args ...any) error { + var scanFailed bool + err := s.brk.DoWithAcceptable(func() error { + return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error { + err := scanFn(v, rows) + if err != nil { + scanFailed = true + } + return err + }, s.query, args...) + }, func(err error) bool { + return scanFailed || s.accept(err) + }) + if errors.Is(err, breaker.ErrServiceUnavailable) { + metricReqErr.Inc("stmt_queryRows", "breaker") + } + + return err +} + // DisableLog disables logging of sql statements, includes info and slow logs. func DisableLog() { logSql.Set(false) diff --git a/core/stores/sqlx/stmt_test.go b/core/stores/sqlx/stmt_test.go index 215bf2b2..84de7577 100644 --- a/core/stores/sqlx/stmt_test.go +++ b/core/stores/sqlx/stmt_test.go @@ -7,7 +7,10 @@ import ( "testing" "time" + "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/breaker" + "github.com/zeromicro/go-zero/core/stores/dbtest" ) var errMockedPlaceholder = errors.New("placeholder") @@ -219,6 +222,74 @@ func TestNilGuard(t *testing.T) { assert.Equal(t, nilGuard{}, guard) } +func TestStmtBreaker(t *testing.T) { + dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { + mock.ExpectPrepare("any") + + conn := NewSqlConnFromDB(db) + stmt, err := conn.Prepare("any") + assert.NoError(t, err) + + var val struct { + Foo int + Bar string + } + for i := 0; i < 1000; i++ { + row := sqlmock.NewRows([]string{"foo"}).AddRow("bar") + mock.ExpectQuery("any").WillReturnRows(row) + err := stmt.QueryRow(&val) + assert.Error(t, err) + assert.NotErrorIs(t, err, breaker.ErrServiceUnavailable) + } + }) + + dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { + mock.ExpectPrepare("any") + conn := NewSqlConnFromDB(db) + stmt, err := conn.Prepare("any") + assert.NoError(t, err) + + for i := 0; i < 1000; i++ { + assert.Error(t, conn.Transact(func(session Session) error { + return nil + })) + } + + var breakerTriggered bool + for i := 0; i < 1000; i++ { + _, err = stmt.Exec("any") + if errors.Is(err, breaker.ErrServiceUnavailable) { + breakerTriggered = true + break + } + } + assert.True(t, breakerTriggered) + }) + + dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { + mock.ExpectPrepare("any") + conn := NewSqlConnFromDB(db) + stmt, err := conn.Prepare("any") + assert.NoError(t, err) + + for i := 0; i < 1000; i++ { + assert.Error(t, conn.Transact(func(session Session) error { + return nil + })) + } + + var breakerTriggered bool + for i := 0; i < 1000; i++ { + err = stmt.QueryRows(&struct{}{}, "any") + if errors.Is(err, breaker.ErrServiceUnavailable) { + breakerTriggered = true + break + } + } + assert.True(t, breakerTriggered) + }) +} + type mockedSessionConn struct { lastInsertId int64 rowsAffected int64 diff --git a/core/stores/sqlx/tx.go b/core/stores/sqlx/tx.go index d983077c..ea2fd2be 100644 --- a/core/stores/sqlx/tx.go +++ b/core/stores/sqlx/tx.go @@ -4,6 +4,8 @@ import ( "context" "database/sql" "fmt" + + "github.com/zeromicro/go-zero/core/breaker" ) type ( @@ -75,6 +77,7 @@ func (t txSession) PrepareCtx(ctx context.Context, q string) (stmtSession StmtSe return statement{ query: q, stmt: stmt, + brk: breaker.NopBreaker(), }, nil } diff --git a/core/stringx/node_fuzz_test.go b/core/stringx/node_fuzz_test.go index 6d752100..d256b513 100644 --- a/core/stringx/node_fuzz_test.go +++ b/core/stringx/node_fuzz_test.go @@ -11,7 +11,7 @@ import ( ) func FuzzNodeFind(f *testing.F) { - rand.Seed(time.Now().UnixNano()) + rand.NewSource(time.Now().UnixNano()) f.Add(10) f.Fuzz(func(t *testing.T, keys int) { diff --git a/core/threading/stablerunner.go b/core/threading/stablerunner.go new file mode 100644 index 00000000..0449dae8 --- /dev/null +++ b/core/threading/stablerunner.go @@ -0,0 +1,105 @@ +package threading + +import ( + "errors" + "runtime" + "sync" + "sync/atomic" +) + +const factor = 10 + +var ( + ErrRunnerClosed = errors.New("runner closed") + + bufSize = runtime.NumCPU() * factor +) + +// StableRunner is a runner that guarantees messages are taken out with the pushed order. +// This runner is typically useful for Kafka consumers with parallel processing. +type StableRunner[I, O any] struct { + handle func(I) O + consumedIndex uint64 + writtenIndex uint64 + ring []*struct { + value chan O + lock sync.Mutex + } + runner *TaskRunner + done chan struct{} +} + +// NewStableRunner returns a new StableRunner with given message processor fn. +func NewStableRunner[I, O any](fn func(I) O) *StableRunner[I, O] { + ring := make([]*struct { + value chan O + lock sync.Mutex + }, bufSize) + for i := 0; i < bufSize; i++ { + ring[i] = &struct { + value chan O + lock sync.Mutex + }{ + value: make(chan O, 1), + } + } + + return &StableRunner[I, O]{ + handle: fn, + ring: ring, + runner: NewTaskRunner(runtime.NumCPU()), + done: make(chan struct{}), + } +} + +// Get returns the next processed message in order. +// This method should be called in one goroutine. +func (r *StableRunner[I, O]) Get() (O, error) { + defer atomic.AddUint64(&r.consumedIndex, 1) + + index := atomic.LoadUint64(&r.consumedIndex) + offset := index % uint64(bufSize) + holder := r.ring[offset] + + select { + case o := <-holder.value: + return o, nil + case <-r.done: + if atomic.LoadUint64(&r.consumedIndex) < atomic.LoadUint64(&r.writtenIndex) { + return <-holder.value, nil + } + + var o O + return o, ErrRunnerClosed + } +} + +// Push pushes the message v into the runner and to be processed concurrently, +// after processed, it will be cached to let caller take it in pushing order. +func (r *StableRunner[I, O]) Push(v I) error { + select { + case <-r.done: + return ErrRunnerClosed + default: + index := atomic.AddUint64(&r.writtenIndex, 1) + offset := (index - 1) % uint64(bufSize) + holder := r.ring[offset] + holder.lock.Lock() + r.runner.Schedule(func() { + defer holder.lock.Unlock() + o := r.handle(v) + holder.value <- o + }) + + return nil + } +} + +// Wait waits all the messages to be processed and taken from inner buffer. +func (r *StableRunner[I, O]) Wait() { + close(r.done) + r.runner.Wait() + for atomic.LoadUint64(&r.consumedIndex) < atomic.LoadUint64(&r.writtenIndex) { + runtime.Gosched() + } +} diff --git a/core/threading/stablerunner_test.go b/core/threading/stablerunner_test.go new file mode 100644 index 00000000..b2da997c --- /dev/null +++ b/core/threading/stablerunner_test.go @@ -0,0 +1,97 @@ +package threading + +import ( + "math/rand" + "sort" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestStableRunner(t *testing.T) { + size := bufSize * 2 + rand.NewSource(time.Now().UnixNano()) + runner := NewStableRunner(func(v int) float64 { + if v == 0 { + time.Sleep(time.Millisecond * 100) + } else { + time.Sleep(time.Millisecond * time.Duration(rand.Intn(10))) + } + return float64(v) + 0.5 + }) + + var waitGroup sync.WaitGroup + waitGroup.Add(1) + go func() { + for i := 0; i < size; i++ { + assert.NoError(t, runner.Push(i)) + } + runner.Wait() + waitGroup.Done() + }() + + values := make([]float64, size) + for i := 0; i < size; i++ { + var err error + values[i], err = runner.Get() + assert.NoError(t, err) + time.Sleep(time.Millisecond) + } + + assert.True(t, sort.Float64sAreSorted(values)) + waitGroup.Wait() + + assert.Equal(t, ErrRunnerClosed, runner.Push(1)) + _, err := runner.Get() + assert.Equal(t, ErrRunnerClosed, err) +} + +func FuzzStableRunner(f *testing.F) { + rand.NewSource(time.Now().UnixNano()) + f.Add(uint64(bufSize)) + f.Fuzz(func(t *testing.T, n uint64) { + runner := NewStableRunner(func(v int) float64 { + if v == 0 { + time.Sleep(time.Millisecond * 100) + } else { + time.Sleep(time.Millisecond * time.Duration(rand.Intn(10))) + } + return float64(v) + 0.5 + }) + + go func() { + for i := 0; i < int(n); i++ { + assert.NoError(t, runner.Push(i)) + } + }() + + values := make([]float64, n) + for i := 0; i < int(n); i++ { + var err error + values[i], err = runner.Get() + assert.NoError(t, err) + } + + runner.Wait() + assert.True(t, sort.Float64sAreSorted(values)) + + // make sure returning errors after runner is closed + assert.Equal(t, ErrRunnerClosed, runner.Push(1)) + _, err := runner.Get() + assert.Equal(t, ErrRunnerClosed, err) + }) +} + +func BenchmarkStableRunner(b *testing.B) { + runner := NewStableRunner(func(v int) float64 { + time.Sleep(time.Millisecond * time.Duration(rand.Intn(10))) + return float64(v) + 0.5 + }) + + for i := 0; i < b.N; i++ { + _ = runner.Push(i) + _, _ = runner.Get() + } +} diff --git a/core/trace/agent.go b/core/trace/agent.go index 2a2e8c37..59e0e23e 100644 --- a/core/trace/agent.go +++ b/core/trace/agent.go @@ -59,7 +59,13 @@ func StartAgent(c Config) { // StopAgent shuts down the span processors in the order they were registered. func StopAgent() { - _ = tp.Shutdown(context.Background()) + lock.Lock() + defer lock.Unlock() + + if tp != nil { + _ = tp.Shutdown(context.Background()) + tp = nil + } } func createExporter(c Config) (sdktrace.SpanExporter, error) { diff --git a/core/trace/message_test.go b/core/trace/message_test.go index 039a5e76..290158f8 100644 --- a/core/trace/message_test.go +++ b/core/trace/message_test.go @@ -34,7 +34,7 @@ type mockSpan struct { options []trace.EventOption } -func (m *mockSpan) End(options ...trace.SpanEndOption) { +func (m *mockSpan) End(_ ...trace.SpanEndOption) { } func (m *mockSpan) AddEvent(name string, options ...trace.EventOption) { @@ -46,20 +46,20 @@ func (m *mockSpan) IsRecording() bool { return false } -func (m *mockSpan) RecordError(err error, options ...trace.EventOption) { +func (m *mockSpan) RecordError(_ error, _ ...trace.EventOption) { } func (m *mockSpan) SpanContext() trace.SpanContext { panic("implement me") } -func (m *mockSpan) SetStatus(code codes.Code, description string) { +func (m *mockSpan) SetStatus(_ codes.Code, _ string) { } -func (m *mockSpan) SetName(name string) { +func (m *mockSpan) SetName(_ string) { } -func (m *mockSpan) SetAttributes(kv ...attribute.KeyValue) { +func (m *mockSpan) SetAttributes(_ ...attribute.KeyValue) { } func (m *mockSpan) TracerProvider() trace.TracerProvider { diff --git a/core/trace/vars.go b/core/trace/vars.go index d8cee290..33d2e6c9 100644 --- a/core/trace/vars.go +++ b/core/trace/vars.go @@ -4,5 +4,5 @@ import "net/http" // TraceIdKey is the trace id header. // https://www.w3.org/TR/trace-context/#trace-id -// May change it to trace-id afterwards. +// May change it to trace-id afterward. var TraceIdKey = http.CanonicalHeaderKey("x-trace-id") diff --git a/go.mod b/go.mod index f138d94e..3e74e352 100644 --- a/go.mod +++ b/go.mod @@ -6,22 +6,22 @@ toolchain go1.21.5 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 - github.com/alicebob/miniredis/v2 v2.31.1 + github.com/alicebob/miniredis/v2 v2.32.1 github.com/fatih/color v1.16.0 github.com/fullstorydev/grpcurl v1.8.9 - github.com/go-sql-driver/mysql v1.7.1 + github.com/go-sql-driver/mysql v1.8.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.6.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/google/uuid v1.6.0 - github.com/jackc/pgx/v5 v5.5.3 + github.com/jackc/pgx/v5 v5.5.5 github.com/jhump/protoreflect v1.15.6 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.1 github.com/prometheus/client_golang v1.18.0 github.com/redis/go-redis/v9 v9.4.0 github.com/spaolacci/murmur3 v1.1.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 go.etcd.io/etcd/api/v3 v3.5.12 go.etcd.io/etcd/client/v3 v3.5.12 go.mongodb.org/mongo-driver v1.13.1 @@ -35,12 +35,12 @@ require ( go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/automaxprocs v1.5.3 go.uber.org/goleak v1.2.1 - golang.org/x/net v0.21.0 - golang.org/x/sys v0.17.0 + golang.org/x/net v0.22.0 + golang.org/x/sys v0.18.0 golang.org/x/time v0.5.0 - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 - google.golang.org/grpc v1.61.1 - google.golang.org/protobuf v1.32.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v2 v2.4.0 @@ -51,6 +51,7 @@ require ( ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bufbuild/protocompile v0.8.0 // indirect @@ -98,7 +99,7 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - github.com/yuin/gopher-lua v1.1.0 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect @@ -106,14 +107,14 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/oauth2 v0.14.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/term v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.110.1 // indirect diff --git a/go.sum b/go.sum index df825bbd..a76e0505 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,12 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.31.1 h1:7XAt0uUg3DtwEKW5ZAGa+K7FZV2DdKQo5K/6TTnfX8Y= -github.com/alicebob/miniredis/v2 v2.31.1/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg= +github.com/alicebob/miniredis/v2 v2.32.1 h1:Bz7CciDnYSaa0mX5xODh6GUITRSx+cVhjNoOR4JssBo= +github.com/alicebob/miniredis/v2 v2.32.1/go.mod h1:AqkLNAfUm0K07J28hnAyyQKf/x0YkCY/g5DCtuL01Mw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -51,8 +52,8 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= +github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -60,15 +61,14 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -94,8 +94,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s= -github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.15.6 h1:WMYJbw2Wo+KOWwZFvgY0jMoVHM6i4XIvRs2RcBj5VmI= @@ -176,15 +176,17 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -198,8 +200,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= -github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= @@ -246,8 +248,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -260,17 +262,17 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -284,12 +286,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -314,18 +316,18 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/mock/depositserver.go b/internal/mock/depositserver.go index 6f7a6275..d8d6030b 100644 --- a/internal/mock/depositserver.go +++ b/internal/mock/depositserver.go @@ -12,7 +12,7 @@ import ( type DepositServer struct{} // Deposit handles the deposit requests. -func (*DepositServer) Deposit(ctx context.Context, req *DepositRequest) (*DepositResponse, error) { +func (*DepositServer) Deposit(_ context.Context, req *DepositRequest) (*DepositResponse, error) { if req.GetAmount() < 0 { return nil, status.Errorf(codes.InvalidArgument, "cannot deposit %v", req.GetAmount()) } diff --git a/readme-cn.md b/readme-cn.md index 941c9f8b..067e79df 100644 --- a/readme-cn.md +++ b/readme-cn.md @@ -120,12 +120,12 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro # docker for amd64 architecture docker pull kevinwan/goctl # run goctl like - docker run --rm -it -v `pwd`:/app kevinwan/goctl goctl --help + docker run --rm -it -v `pwd`:/app kevinwan/goctl --help # docker for arm64(Mac) architecture docker pull kevinwan/goctl:latest-arm64 # run goctl like - docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 goctl --help + docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest-arm64 --help ``` 确保 goctl 可执行 @@ -300,14 +300,16 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电 >96. Sonderbase Technologies >97. 上海荣时信息科技有限公司 >98. 上海同犀智能科技有限公司 +>99. 新华三技术有限公司 +>100. 上海邑脉科技有限公司 如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。 ## 10. CNCF 云原生技术全景图
-
-
+
+
-
-
+
+