mirror of https://github.com/go-redis/redis.git
Merge branch 'master' into ankit/add-custom-label-support
This commit is contained in:
commit
5c2b307cc7
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
28
README.md
28
README.md
|
@ -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!
|
||||||
|
|
|
@ -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...)
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
10
command.go
10
command.go
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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())
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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 {}) {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
})
|
||||||
|
}
|
|
@ -62,9 +62,10 @@ type ClusterOptions struct {
|
||||||
|
|
||||||
OnConnect func(ctx context.Context, cn *Conn) error
|
OnConnect func(ctx context.Context, cn *Conn) error
|
||||||
|
|
||||||
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
|
||||||
|
@ -271,9 +272,10 @@ func (opt *ClusterOptions) clientOptions() *Options {
|
||||||
Dialer: opt.Dialer,
|
Dialer: opt.Dialer,
|
||||||
OnConnect: opt.OnConnect,
|
OnConnect: opt.OnConnect,
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
24
redis.go
24
redis.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue