Merge branch 'master' into ankit/add-custom-label-support

This commit is contained in:
Ankit Chahar 2024-03-06 19:48:07 +05:30 committed by GitHub
commit 5c2b307cc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 345 additions and 53 deletions

View File

@ -2,6 +2,7 @@ ACLs
autoload autoload
autoloader autoloader
autoloading autoloading
analytics
Autoloading Autoloading
backend backend
backends backends
@ -13,6 +14,7 @@ customizable
Customizable Customizable
dataset dataset
de de
DisableIdentity
ElastiCache ElastiCache
extensibility extensibility
FPM FPM
@ -43,6 +45,7 @@ RocksDB
runtime runtime
SHA SHA
sharding sharding
SETNAME
SSL SSL
struct struct
stunnel stunnel

View File

@ -23,4 +23,4 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v3 uses: golangci/golangci-lint-action@v4

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# Drafts your next Release notes as Pull Requests are merged into "master" # Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v5 - uses: release-drafter/release-drafter@v6
with: with:
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
config-name: release-drafter-config.yml config-name: release-drafter-config.yml

View File

@ -8,7 +8,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Check Spelling - name: Check Spelling
uses: rojopolis/spellcheck-github-actions@0.35.0 uses: rojopolis/spellcheck-github-actions@0.36.0
with: with:
config_path: .github/spellcheck-settings.yml config_path: .github/spellcheck-settings.yml
task_name: Markdown task_name: Markdown

View File

@ -16,7 +16,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
go-version: [1.21.x] go-version: [1.21.x]
re-build: ["7.2.4-92"] re-build: ["7.4.2-54"]
steps: steps:
- name: Checkout code - name: Checkout code
@ -36,7 +36,7 @@ jobs:
- name: Build cluster - name: Build cluster
working-directory: redis-ee working-directory: redis-ee
env: env:
IMAGE: "redislabs/redis-internal:${{ matrix.re-build }}" IMAGE: "redislabs/redis:${{ matrix.re-build }}"
RE_USERNAME: ${{ secrets.RE_USERNAME }} RE_USERNAME: ${{ secrets.RE_USERNAME }}
RE_PASS: ${{ secrets.RE_PASS }} RE_PASS: ${{ secrets.RE_PASS }}
RE_CLUSTER_NAME: ${{ secrets.RE_CLUSTER_NAME }} RE_CLUSTER_NAME: ${{ secrets.RE_CLUSTER_NAME }}

View File

@ -51,8 +51,8 @@ key value NoSQL database that uses RocksDB as storage engine and is compatible w
## Features ## Features
- Redis 3 commands except QUIT, MONITOR, and SYNC. - Redis commands except QUIT and SYNC.
- Automatic connection pooling with - Automatic connection pooling.
- [Pub/Sub](https://redis.uptrace.dev/guide/go-redis-pubsub.html). - [Pub/Sub](https://redis.uptrace.dev/guide/go-redis-pubsub.html).
- [Pipelines and transactions](https://redis.uptrace.dev/guide/go-redis-pipelines.html). - [Pipelines and transactions](https://redis.uptrace.dev/guide/go-redis-pipelines.html).
- [Scripting](https://redis.uptrace.dev/guide/lua-scripting.html). - [Scripting](https://redis.uptrace.dev/guide/lua-scripting.html).
@ -161,6 +161,30 @@ func ExampleClient() *redis.Client {
``` ```
### Advanced Configuration
go-redis supports extending the client identification phase to allow projects to send their own custom client identification.
#### Default Client Identification
By default, go-redis automatically sends the client library name and version during the connection process. This feature is available in redis-server as of version 7.2. As a result, the command is "fire and forget", meaning it should fail silently, in the case that the redis server does not support this feature.
#### Disabling Identity Verification
When connection identity verification is not required or needs to be explicitly disabled, a `DisableIndentity` configuration option exists. In V10 of this library, `DisableIndentity` will become `DisableIdentity` in order to fix the associated typo.
To disable verification, set the `DisableIndentity` option to `true` in the Redis client options:
```go
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
DisableIndentity: true, // Disable set-info on connect
})
```
## Contributing ## Contributing
Please see [out contributing guidelines](CONTRIBUTING.md) to help us improve this library! Please see [out contributing guidelines](CONTRIBUTING.md) to help us improve this library!

View File

@ -2,6 +2,7 @@ package redis
import ( import (
"context" "context"
"errors"
) )
type BitMapCmdable interface { type BitMapCmdable interface {
@ -37,15 +38,28 @@ func (c cmdable) SetBit(ctx context.Context, key string, offset int64, value int
type BitCount struct { type BitCount struct {
Start, End int64 Start, End int64
Unit string // BYTE(default) | BIT
} }
const BitCountIndexByte string = "BYTE"
const BitCountIndexBit string = "BIT"
func (c cmdable) BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd { func (c cmdable) BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd {
args := []interface{}{"bitcount", key} args := []interface{}{"bitcount", key}
if bitCount != nil { if bitCount != nil {
if bitCount.Unit == "" {
bitCount.Unit = "BYTE"
}
if bitCount.Unit != BitCountIndexByte && bitCount.Unit != BitCountIndexBit {
cmd := NewIntCmd(ctx)
cmd.SetErr(errors.New("redis: invalid bitcount index"))
return cmd
}
args = append( args = append(
args, args,
bitCount.Start, bitCount.Start,
bitCount.End, bitCount.End,
string(bitCount.Unit),
) )
} }
cmd := NewIntCmd(ctx, args...) cmd := NewIntCmd(ctx, args...)

98
bitmap_commands_test.go Normal file
View File

@ -0,0 +1,98 @@
package redis_test
import (
. "github.com/bsm/ginkgo/v2"
. "github.com/bsm/gomega"
"github.com/redis/go-redis/v9"
)
type bitCountExpected struct {
Start int64
End int64
Expected int64
}
var _ = Describe("BitCountBite", func() {
var client *redis.Client
key := "bit_count_test"
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
values := []int{0, 1, 0, 0, 1, 0, 1, 0, 1, 1}
for i, v := range values {
cmd := client.SetBit(ctx, key, int64(i), v)
Expect(cmd.Err()).NotTo(HaveOccurred())
}
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("bit count bite", func() {
var expected = []bitCountExpected{
{0, 0, 0},
{0, 1, 1},
{0, 2, 1},
{0, 3, 1},
{0, 4, 2},
{0, 5, 2},
{0, 6, 3},
{0, 7, 3},
{0, 8, 4},
{0, 9, 5},
}
for _, e := range expected {
cmd := client.BitCount(ctx, key, &redis.BitCount{Start: e.Start, End: e.End, Unit: redis.BitCountIndexBit})
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.Val()).To(Equal(e.Expected))
}
})
})
var _ = Describe("BitCountByte", func() {
var client *redis.Client
key := "bit_count_test"
BeforeEach(func() {
client = redis.NewClient(redisOptions())
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
values := []int{0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
for i, v := range values {
cmd := client.SetBit(ctx, key, int64(i), v)
Expect(cmd.Err()).NotTo(HaveOccurred())
}
})
AfterEach(func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("bit count byte", func() {
var expected = []bitCountExpected{
{0, 0, 1},
{0, 1, 3},
}
for _, e := range expected {
cmd := client.BitCount(ctx, key, &redis.BitCount{Start: e.Start, End: e.End, Unit: redis.BitCountIndexByte})
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.Val()).To(Equal(e.Expected))
}
})
It("bit count byte with no unit specified", func() {
var expected = []bitCountExpected{
{0, 0, 1},
{0, 1, 3},
}
for _, e := range expected {
cmd := client.BitCount(ctx, key, &redis.BitCount{Start: e.Start, End: e.End})
Expect(cmd.Err()).NotTo(HaveOccurred())
Expect(cmd.Val()).To(Equal(e.Expected))
}
})
})

View File

@ -5310,6 +5310,16 @@ type LibraryInfo struct {
LibVer *string LibVer *string
} }
// WithLibraryName returns a valid LibraryInfo with library name only.
func WithLibraryName(libName string) LibraryInfo {
return LibraryInfo{LibName: &libName}
}
// WithLibraryVersion returns a valid LibraryInfo with library version only.
func WithLibraryVersion(libVer string) LibraryInfo {
return LibraryInfo{LibVer: &libVer}
}
// ------------------------------------------- // -------------------------------------------
type InfoCmd struct { type InfoCmd struct {

View File

@ -309,7 +309,7 @@ func (c statefulCmdable) ClientSetInfo(ctx context.Context, info LibraryInfo) *S
var cmd *StatusCmd var cmd *StatusCmd
if info.LibName != nil { if info.LibName != nil {
libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, runtime.Version()) libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, internal.ReplaceSpaces(runtime.Version()))
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName) cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName)
} else { } else {
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer) cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer)

View File

@ -248,7 +248,7 @@ var _ = Describe("Commands", func() {
// Test setting the libName // Test setting the libName
libName := "go-redis" libName := "go-redis"
libInfo := redis.LibraryInfo{LibName: &libName} libInfo := redis.WithLibraryName(libName)
setInfo := pipe.ClientSetInfo(ctx, libInfo) setInfo := pipe.ClientSetInfo(ctx, libInfo)
_, err := pipe.Exec(ctx) _, err := pipe.Exec(ctx)
@ -258,7 +258,7 @@ var _ = Describe("Commands", func() {
// Test setting the libVer // Test setting the libVer
libVer := "vX.x" libVer := "vX.x"
libInfo = redis.LibraryInfo{LibVer: &libVer} libInfo = redis.WithLibraryVersion(libVer)
setInfo = pipe.ClientSetInfo(ctx, libInfo) setInfo = pipe.ClientSetInfo(ctx, libInfo)
_, err = pipe.Exec(ctx) _, err = pipe.Exec(ctx)
@ -676,7 +676,7 @@ var _ = Describe("Commands", func() {
Expect(get.Val()).To(Equal("hello")) Expect(get.Val()).To(Equal("hello"))
}) })
It("should Object", func() { It("should Object", Label("NonRedisEnterprise"), func() {
start := time.Now() start := time.Now()
set := client.Set(ctx, "key", "hello", 0) set := client.Set(ctx, "key", "hello", 0)
Expect(set.Err()).NotTo(HaveOccurred()) Expect(set.Err()).NotTo(HaveOccurred())
@ -686,6 +686,11 @@ var _ = Describe("Commands", func() {
Expect(refCount.Err()).NotTo(HaveOccurred()) Expect(refCount.Err()).NotTo(HaveOccurred())
Expect(refCount.Val()).To(Equal(int64(1))) Expect(refCount.Val()).To(Equal(int64(1)))
client.ConfigSet(ctx, "maxmemory-policy", "volatile-lfu")
freq := client.ObjectFreq(ctx, "key")
Expect(freq.Err()).NotTo(HaveOccurred())
client.ConfigSet(ctx, "maxmemory-policy", "noeviction") // default
err := client.ObjectEncoding(ctx, "key").Err() err := client.ObjectEncoding(ctx, "key").Err()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())

View File

@ -5,7 +5,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/v9 => ../..
require ( require (
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
) )

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.4.0 require github.com/redis/go-redis/v9 v9.5.1
require ( require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.4.0 require github.com/redis/go-redis/v9 v9.5.1
require ( require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -9,8 +9,8 @@ replace github.com/redis/go-redis/extra/redisotel/v9 => ../../extra/redisotel
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd
require ( require (
github.com/redis/go-redis/extra/redisotel/v9 v9.4.0 github.com/redis/go-redis/extra/redisotel/v9 v9.5.1
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
github.com/uptrace/uptrace-go v1.21.0 github.com/uptrace/uptrace-go v1.21.0
go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel v1.21.0
) )
@ -23,7 +23,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.4.0 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.5.1 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.43.0 // indirect

View File

@ -4,7 +4,7 @@ go 1.18
replace github.com/redis/go-redis/v9 => ../.. replace github.com/redis/go-redis/v9 => ../..
require github.com/redis/go-redis/v9 v9.4.0 require github.com/redis/go-redis/v9 v9.5.1
require ( require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect

View File

@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
require ( require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
) )
require ( require (

View File

@ -11,6 +11,7 @@ import (
type Model struct { type Model struct {
Str1 string `redis:"str1"` Str1 string `redis:"str1"`
Str2 string `redis:"str2"` Str2 string `redis:"str2"`
Bytes []byte `redis:"bytes"`
Int int `redis:"int"` Int int `redis:"int"`
Bool bool `redis:"bool"` Bool bool `redis:"bool"`
Ignored struct{} `redis:"-"` Ignored struct{} `redis:"-"`
@ -22,6 +23,7 @@ func main() {
rdb := redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
Addr: ":6379", Addr: ":6379",
}) })
_ = rdb.FlushDB(ctx).Err()
// Set some fields. // Set some fields.
if _, err := rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error { if _, err := rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error {
@ -29,6 +31,7 @@ func main() {
rdb.HSet(ctx, "key", "str2", "world") rdb.HSet(ctx, "key", "str2", "world")
rdb.HSet(ctx, "key", "int", 123) rdb.HSet(ctx, "key", "int", 123)
rdb.HSet(ctx, "key", "bool", 1) rdb.HSet(ctx, "key", "bool", 1)
rdb.HSet(ctx, "key", "bytes", []byte("this is bytes !"))
return nil return nil
}); err != nil { }); err != nil {
panic(err) panic(err)
@ -47,5 +50,28 @@ func main() {
} }
spew.Dump(model1) spew.Dump(model1)
// Output:
// (main.Model) {
// Str1: (string) (len=5) "hello",
// Str2: (string) (len=5) "world",
// Bytes: ([]uint8) (len=15 cap=16) {
// 00000000 74 68 69 73 20 69 73 20 62 79 74 65 73 20 21 |this is bytes !|
// },
// Int: (int) 123,
// Bool: (bool) true,
// Ignored: (struct {}) {
// }
// }
spew.Dump(model2) spew.Dump(model2)
// Output:
// (main.Model) {
// Str1: (string) (len=5) "hello",
// Str2: (string) "",
// Bytes: ([]uint8) <nil>,
// Int: (int) 123,
// Bool: (bool) false,
// Ignored: (struct {}) {
// }
// }
} }

View File

@ -154,7 +154,7 @@ func ExampleClient() {
// missing_key does not exist // missing_key does not exist
} }
func ExampleConn() { func ExampleConn_name() {
conn := rdb.Conn() conn := rdb.Conn()
err := conn.ClientSetName(ctx, "foobar").Err() err := conn.ClientSetName(ctx, "foobar").Err()
@ -175,6 +175,28 @@ func ExampleConn() {
// Output: foobar // Output: foobar
} }
func ExampleConn_client_setInfo_libraryVersion() {
conn := rdb.Conn()
err := conn.ClientSetInfo(ctx, redis.WithLibraryVersion("1.2.3")).Err()
if err != nil {
panic(err)
}
// Open other connections.
for i := 0; i < 10; i++ {
go rdb.Ping(ctx)
}
s, err := conn.ClientInfo(ctx).Result()
if err != nil {
panic(err)
}
fmt.Println(s.LibVer)
// Output: 1.2.3
}
func ExampleClient_Set() { func ExampleClient_Set() {
// Last argument is expiration. Zero means the key has no // Last argument is expiration. Zero means the key has no
// expiration time. // expiration time.

View File

@ -8,7 +8,7 @@ replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
require ( require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.4.0 github.com/redis/go-redis/extra/rediscmd/v9 v9.5.1
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
go.opencensus.io v0.24.0 go.opencensus.io v0.24.0
) )

View File

@ -7,5 +7,5 @@ replace github.com/redis/go-redis/v9 => ../..
require ( require (
github.com/bsm/ginkgo/v2 v2.7.0 github.com/bsm/ginkgo/v2 v2.7.0
github.com/bsm/gomega v1.26.0 github.com/bsm/gomega v1.26.0
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
) )

View File

@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
require ( require (
github.com/redis/go-redis/extra/rediscmd/v9 v9.4.0 github.com/redis/go-redis/extra/rediscmd/v9 v9.5.1
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/metric v1.16.0 go.opentelemetry.io/otel/metric v1.16.0
go.opentelemetry.io/otel/sdk v1.16.0 go.opentelemetry.io/otel/sdk v1.16.0

View File

@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
require ( require (
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.14.0
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.5.1
) )
require ( require (

View File

@ -19,6 +19,7 @@ type GenericCmdable interface {
Keys(ctx context.Context, pattern string) *StringSliceCmd Keys(ctx context.Context, pattern string) *StringSliceCmd
Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd
Move(ctx context.Context, key string, db int) *BoolCmd Move(ctx context.Context, key string, db int) *BoolCmd
ObjectFreq(ctx context.Context, key string) *IntCmd
ObjectRefCount(ctx context.Context, key string) *IntCmd ObjectRefCount(ctx context.Context, key string) *IntCmd
ObjectEncoding(ctx context.Context, key string) *StringCmd ObjectEncoding(ctx context.Context, key string) *StringCmd
ObjectIdleTime(ctx context.Context, key string) *DurationCmd ObjectIdleTime(ctx context.Context, key string) *DurationCmd
@ -159,6 +160,12 @@ func (c cmdable) Move(ctx context.Context, key string, db int) *BoolCmd {
return cmd return cmd
} }
func (c cmdable) ObjectFreq(ctx context.Context, key string) *IntCmd {
cmd := NewIntCmd(ctx, "object", "freq", key)
_ = c(ctx, cmd)
return cmd
}
func (c cmdable) ObjectRefCount(ctx context.Context, key string) *IntCmd { func (c cmdable) ObjectRefCount(ctx context.Context, key string) *IntCmd {
cmd := NewIntCmd(ctx, "object", "refcount", key) cmd := NewIntCmd(ctx, "object", "refcount", key)
_ = c(ctx, cmd) _ = c(ctx, cmd)

View File

@ -168,9 +168,12 @@ func (p *ConnPool) newConn(ctx context.Context, pooled bool) (*Conn, error) {
return nil, ErrClosed return nil, ErrClosed
} }
p.connsMu.Lock()
if p.cfg.MaxActiveConns > 0 && p.poolSize >= p.cfg.MaxActiveConns { if p.cfg.MaxActiveConns > 0 && p.poolSize >= p.cfg.MaxActiveConns {
p.connsMu.Unlock()
return nil, ErrPoolExhausted return nil, ErrPoolExhausted
} }
p.connsMu.Unlock()
cn, err := p.dialConn(ctx, pooled) cn, err := p.dialConn(ctx, pooled)
if err != nil { if err != nil {
@ -180,6 +183,11 @@ func (p *ConnPool) newConn(ctx context.Context, pooled bool) (*Conn, error) {
p.connsMu.Lock() p.connsMu.Lock()
defer p.connsMu.Unlock() defer p.connsMu.Unlock()
if p.cfg.MaxActiveConns > 0 && p.poolSize >= p.cfg.MaxActiveConns {
_ = cn.Close()
return nil, ErrPoolExhausted
}
p.conns = append(p.conns, cn) p.conns = append(p.conns, cn)
if pooled { if pooled {
// If pool is full remove the cn on next Put. // If pool is full remove the cn on next Put.

View File

@ -2,6 +2,7 @@ package internal
import ( import (
"context" "context"
"strings"
"time" "time"
"github.com/redis/go-redis/v9/internal/util" "github.com/redis/go-redis/v9/internal/util"
@ -44,3 +45,22 @@ func isLower(s string) bool {
} }
return true return true
} }
func ReplaceSpaces(s string) string {
// Pre-allocate a builder with the same length as s to minimize allocations.
// This is a basic optimization; adjust the initial size based on your use case.
var builder strings.Builder
builder.Grow(len(s))
for _, char := range s {
if char == ' ' {
// Replace space with a hyphen.
builder.WriteRune('-')
} else {
// Copy the character as-is.
builder.WriteRune(char)
}
}
return builder.String()
}

53
internal/util_test.go Normal file
View File

@ -0,0 +1,53 @@
package internal
import (
"strings"
"testing"
. "github.com/bsm/ginkgo/v2"
. "github.com/bsm/gomega"
)
func BenchmarkToLowerStd(b *testing.B) {
str := "AaBbCcDdEeFfGgHhIiJjKk"
for i := 0; i < b.N; i++ {
_ = strings.ToLower(str)
}
}
// util.ToLower is 3x faster than strings.ToLower.
func BenchmarkToLowerInternal(b *testing.B) {
str := "AaBbCcDdEeFfGgHhIiJjKk"
for i := 0; i < b.N; i++ {
_ = ToLower(str)
}
}
func TestToLower(t *testing.T) {
It("toLower", func() {
str := "AaBbCcDdEeFfGg"
Expect(ToLower(str)).To(Equal(strings.ToLower(str)))
str = "ABCDE"
Expect(ToLower(str)).To(Equal(strings.ToLower(str)))
str = "ABCDE"
Expect(ToLower(str)).To(Equal(strings.ToLower(str)))
str = "abced"
Expect(ToLower(str)).To(Equal(strings.ToLower(str)))
})
}
func TestIsLower(t *testing.T) {
It("isLower", func() {
str := "AaBbCcDdEeFfGg"
Expect(isLower(str)).To(BeFalse())
str = "ABCDE"
Expect(isLower(str)).To(BeFalse())
str = "abcdefg"
Expect(isLower(str)).To(BeTrue())
})
}

View File

@ -65,6 +65,7 @@ type ClusterOptions struct {
Protocol int Protocol int
Username string Username string
Password string Password string
CredentialsProvider func() (username string, password string)
MaxRetries int MaxRetries int
MinRetryBackoff time.Duration MinRetryBackoff time.Duration
@ -274,6 +275,7 @@ func (opt *ClusterOptions) clientOptions() *Options {
Protocol: opt.Protocol, Protocol: opt.Protocol,
Username: opt.Username, Username: opt.Username,
Password: opt.Password, Password: opt.Password,
CredentialsProvider: opt.CredentialsProvider,
MaxRetries: opt.MaxRetries, MaxRetries: opt.MaxRetries,
MinRetryBackoff: opt.MinRetryBackoff, MinRetryBackoff: opt.MinRetryBackoff,

View File

@ -1,8 +0,0 @@
{
"name": "redis",
"version": "9.4.0",
"main": "index.js",
"repository": "git@github.com:redis/go-redis.git",
"author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",
"license": "BSD-2-clause"
}

View File

@ -312,17 +312,7 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
// difficult to rely on error strings to determine all results. // difficult to rely on error strings to determine all results.
return err return err
} }
if !c.opt.DisableIndentity {
libName := ""
libVer := Version()
if c.opt.IdentitySuffix != "" {
libName = c.opt.IdentitySuffix
}
libInfo := LibraryInfo{LibName: &libName}
conn.ClientSetInfo(ctx, libInfo)
libInfo = LibraryInfo{LibVer: &libVer}
conn.ClientSetInfo(ctx, libInfo)
}
_, err := conn.Pipelined(ctx, func(pipe Pipeliner) error { _, err := conn.Pipelined(ctx, func(pipe Pipeliner) error {
if !auth && password != "" { if !auth && password != "" {
if username != "" { if username != "" {
@ -350,6 +340,18 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
return err return err
} }
if !c.opt.DisableIndentity {
libName := ""
libVer := Version()
if c.opt.IdentitySuffix != "" {
libName = c.opt.IdentitySuffix
}
p := conn.Pipeline()
p.ClientSetInfo(ctx, WithLibraryName(libName))
p.ClientSetInfo(ctx, WithLibraryVersion(libVer))
_, _ = p.Exec(ctx)
}
if c.opt.OnConnect != nil { if c.opt.OnConnect != nil {
return c.opt.OnConnect(ctx, conn) return c.opt.OnConnect(ctx, conn)
} }

View File

@ -153,6 +153,9 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
ConnMaxLifetime: opt.ConnMaxLifetime, ConnMaxLifetime: opt.ConnMaxLifetime,
TLSConfig: opt.TLSConfig, TLSConfig: opt.TLSConfig,
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
} }
} }
@ -190,6 +193,9 @@ func (opt *FailoverOptions) clusterOptions() *ClusterOptions {
ConnMaxLifetime: opt.ConnMaxLifetime, ConnMaxLifetime: opt.ConnMaxLifetime,
TLSConfig: opt.TLSConfig, TLSConfig: opt.TLSConfig,
DisableIndentity: opt.DisableIndentity,
IdentitySuffix: opt.IdentitySuffix,
} }
} }

View File

@ -324,7 +324,7 @@ var _ = Describe("SentinelAclAuth", func() {
BeforeEach(func() { BeforeEach(func() {
authCmd := redis.NewStatusCmd(ctx, "ACL", "SETUSER", aclSentinelUsername, "ON", authCmd := redis.NewStatusCmd(ctx, "ACL", "SETUSER", aclSentinelUsername, "ON",
">"+aclSentinelPassword, "-@all", "+auth", "+client|getname", "+client|id", "+client|setname", ">"+aclSentinelPassword, "-@all", "+auth", "+client|getname", "+client|id", "+client|setname",
"+command", "+hello", "+ping", "+role", "+sentinel|get-master-addr-by-name", "+sentinel|master", "+command", "+hello", "+ping", "+client|setinfo", "+role", "+sentinel|get-master-addr-by-name", "+sentinel|master",
"+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels") "+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels")
for _, process := range sentinels() { for _, process := range sentinels() {

View File

@ -2,5 +2,5 @@ package redis
// Version is the current release version. // Version is the current release version.
func Version() string { func Version() string {
return "9.4.0" return "9.5.1"
} }