mirror of
https://github.com/redis/go-redis.git
synced 2025-12-02 06:22:31 +03:00
Merge branch 'master' into ndyakov/optional-logger
This commit is contained in:
4
.github/workflows/stale-issues.yml
vendored
4
.github/workflows/stale-issues.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
# First step: Handle regular issues (excluding needs-information)
|
||||
- name: Mark regular issues as stale
|
||||
uses: actions/stale@v9
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
# Second step: Handle needs-information issues with accelerated timeline
|
||||
- name: Mark needs-information issues as stale
|
||||
uses: actions/stale@v9
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# Release Notes
|
||||
|
||||
# 9.17.1 (2025-11-25)
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
- add wait to keyless commands list ([#3615](https://github.com/redis/go-redis/pull/3615)) by [@marcoferrer](https://github.com/marcoferrer)
|
||||
- fix(time): remove cached time optimization ([#3611](https://github.com/redis/go-redis/pull/3611)) by [@ndyakov](https://github.com/ndyakov)
|
||||
|
||||
## 🧰 Maintenance
|
||||
|
||||
- chore(deps): bump golangci/golangci-lint-action from 9.0.0 to 9.1.0 ([#3609](https://github.com/redis/go-redis/pull/3609))
|
||||
- chore(deps): bump actions/checkout from 5 to 6 ([#3610](https://github.com/redis/go-redis/pull/3610))
|
||||
- chore(script): fix help call in tag.sh ([#3606](https://github.com/redis/go-redis/pull/3606)) by [@ndyakov](https://github.com/ndyakov)
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
[@marcoferrer](https://github.com/marcoferrer) and [@ndyakov](https://github.com/ndyakov)
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/redis/go-redis/compare/v9.17.0...v9.17.1
|
||||
|
||||
# 9.17.0 (2025-11-19)
|
||||
|
||||
## 🚀 Highlights
|
||||
|
||||
144
example/cluster-mget/README.md
Normal file
144
example/cluster-mget/README.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Redis Cluster MGET Example
|
||||
|
||||
This example demonstrates how to use the Redis Cluster client with the `MGET` command to retrieve multiple keys efficiently.
|
||||
|
||||
## Overview
|
||||
|
||||
The example shows:
|
||||
- Creating a Redis Cluster client
|
||||
- Setting 10 keys with individual `SET` commands
|
||||
- Retrieving all 10 keys in a single operation using `MGET`
|
||||
- Validating that the retrieved values match the expected values
|
||||
- Cleaning up by deleting the test keys
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need a running Redis Cluster. The example expects cluster nodes at:
|
||||
- `localhost:7000`
|
||||
- `localhost:7001`
|
||||
- `localhost:7002`
|
||||
|
||||
### Setting up a Redis Cluster (using Docker)
|
||||
|
||||
If you don't have a Redis Cluster running, you can use the docker-compose setup from the repository root:
|
||||
|
||||
```bash
|
||||
# From the go-redis repository root
|
||||
docker compose --profile cluster up -d
|
||||
```
|
||||
|
||||
This will start a Redis Cluster with nodes on ports 16600-16605.
|
||||
|
||||
If using the docker-compose cluster, update the `Addrs` in `main.go` to:
|
||||
```go
|
||||
Addrs: []string{
|
||||
"localhost:16600",
|
||||
"localhost:16601",
|
||||
"localhost:16602",
|
||||
},
|
||||
```
|
||||
|
||||
## Running the Example
|
||||
|
||||
```bash
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## Expected Output
|
||||
|
||||
```
|
||||
✓ Connected to Redis cluster
|
||||
|
||||
=== Setting 10 keys ===
|
||||
✓ SET key0 = value0
|
||||
✓ SET key1 = value1
|
||||
✓ SET key2 = value2
|
||||
✓ SET key3 = value3
|
||||
✓ SET key4 = value4
|
||||
✓ SET key5 = value5
|
||||
✓ SET key6 = value6
|
||||
✓ SET key7 = value7
|
||||
✓ SET key8 = value8
|
||||
✓ SET key9 = value9
|
||||
|
||||
=== Retrieving keys with MGET ===
|
||||
|
||||
=== Validating MGET results ===
|
||||
✓ key0: value0
|
||||
✓ key1: value1
|
||||
✓ key2: value2
|
||||
✓ key3: value3
|
||||
✓ key4: value4
|
||||
✓ key5: value5
|
||||
✓ key6: value6
|
||||
✓ key7: value7
|
||||
✓ key8: value8
|
||||
✓ key9: value9
|
||||
|
||||
=== Summary ===
|
||||
✓ All values retrieved successfully and match expected values!
|
||||
|
||||
=== Cleaning up ===
|
||||
✓ Cleanup complete
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### MGET Command
|
||||
|
||||
`MGET` (Multiple GET) is a Redis command that retrieves the values of multiple keys in a single operation. This is more efficient than executing multiple individual `GET` commands.
|
||||
|
||||
**Syntax:**
|
||||
```go
|
||||
result, err := rdb.MGet(ctx, key1, key2, key3, ...).Result()
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
- A slice of `interface{}` values
|
||||
- Each value corresponds to a key in the same order
|
||||
- `nil` is returned for keys that don't exist
|
||||
|
||||
### Cluster Client
|
||||
|
||||
The `ClusterClient` automatically handles:
|
||||
- Distributing keys across cluster nodes based on hash slots
|
||||
- Following cluster redirects
|
||||
- Maintaining connections to all cluster nodes
|
||||
- Retrying operations on cluster topology changes
|
||||
|
||||
For `MGET` operations in a cluster, the client may need to split the request across multiple nodes if the keys map to different hash slots.
|
||||
|
||||
## Code Highlights
|
||||
|
||||
```go
|
||||
// Create cluster client
|
||||
rdb := redis.NewClusterClient(&redis.ClusterOptions{
|
||||
Addrs: []string{
|
||||
"localhost:7000",
|
||||
"localhost:7001",
|
||||
"localhost:7002",
|
||||
},
|
||||
})
|
||||
|
||||
// Set individual keys
|
||||
for i := 0; i < 10; i++ {
|
||||
err := rdb.Set(ctx, fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i), 0).Err()
|
||||
// handle error
|
||||
}
|
||||
|
||||
// Retrieve all keys with MGET
|
||||
result, err := rdb.MGet(ctx, keys...).Result()
|
||||
|
||||
// Validate results
|
||||
for i, val := range result {
|
||||
actualValue, ok := val.(string)
|
||||
// validate actualValue matches expected
|
||||
}
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Redis MGET Documentation](https://redis.io/commands/mget/)
|
||||
- [Redis Cluster Specification](https://redis.io/topics/cluster-spec)
|
||||
- [go-redis Documentation](https://redis.uptrace.dev/)
|
||||
|
||||
12
example/cluster-mget/go.mod
Normal file
12
example/cluster-mget/go.mod
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/redis/go-redis/example/cluster-mget
|
||||
|
||||
go 1.18
|
||||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.16.0
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
)
|
||||
6
example/cluster-mget/go.sum
Normal file
6
example/cluster-mget/go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
108
example/cluster-mget/main.go
Normal file
108
example/cluster-mget/main.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a cluster client
|
||||
rdb := redis.NewClusterClient(&redis.ClusterOptions{
|
||||
Addrs: []string{
|
||||
"localhost:16600",
|
||||
"localhost:16601",
|
||||
"localhost:16602",
|
||||
"localhost:16603",
|
||||
"localhost:16604",
|
||||
"localhost:16605",
|
||||
},
|
||||
})
|
||||
defer rdb.Close()
|
||||
|
||||
// Test connection
|
||||
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||
panic(fmt.Sprintf("Failed to connect to Redis cluster: %v", err))
|
||||
}
|
||||
|
||||
fmt.Println("✓ Connected to Redis cluster")
|
||||
|
||||
// Define 10 keys and values
|
||||
keys := make([]string, 10)
|
||||
values := make([]string, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
keys[i] = fmt.Sprintf("key%d", i)
|
||||
values[i] = fmt.Sprintf("value%d", i)
|
||||
}
|
||||
|
||||
// Set all 10 keys
|
||||
fmt.Println("\n=== Setting 10 keys ===")
|
||||
for i := 0; i < 10; i++ {
|
||||
err := rdb.Set(ctx, keys[i], values[i], 0).Err()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to set %s: %v", keys[i], err))
|
||||
}
|
||||
fmt.Printf("✓ SET %s = %s\n", keys[i], values[i])
|
||||
}
|
||||
|
||||
/*
|
||||
// Retrieve all keys using MGET
|
||||
fmt.Println("\n=== Retrieving keys with MGET ===")
|
||||
result, err := rdb.MGet(ctx, keys...).Result()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to execute MGET: %v", err))
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// Validate the results
|
||||
fmt.Println("\n=== Validating MGET results ===")
|
||||
allValid := true
|
||||
for i, val := range result {
|
||||
expectedValue := values[i]
|
||||
actualValue, ok := val.(string)
|
||||
|
||||
if !ok {
|
||||
fmt.Printf("✗ %s: expected string, got %T\n", keys[i], val)
|
||||
allValid = false
|
||||
continue
|
||||
}
|
||||
|
||||
if actualValue != expectedValue {
|
||||
fmt.Printf("✗ %s: expected '%s', got '%s'\n", keys[i], expectedValue, actualValue)
|
||||
allValid = false
|
||||
} else {
|
||||
fmt.Printf("✓ %s: %s\n", keys[i], actualValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
fmt.Println("\n=== Summary ===")
|
||||
if allValid {
|
||||
fmt.Println("✓ All values retrieved successfully and match expected values!")
|
||||
} else {
|
||||
fmt.Println("✗ Some values did not match expected values")
|
||||
}
|
||||
*/
|
||||
|
||||
// Clean up - delete the keys
|
||||
fmt.Println("\n=== Cleaning up ===")
|
||||
for _, key := range keys {
|
||||
if err := rdb.Del(ctx, key).Err(); err != nil {
|
||||
fmt.Printf("Warning: Failed to delete %s: %v\n", key, err)
|
||||
}
|
||||
}
|
||||
fmt.Println("✓ Cleanup complete")
|
||||
|
||||
err := rdb.Set(ctx, "{tag}exists", "asdf",0).Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
val, err := rdb.Get(ctx, "{tag}nilkeykey1").Result()
|
||||
fmt.Printf("\nval: %+v err: %+v\n", val, err)
|
||||
valm, err := rdb.MGet(ctx, "{tag}nilkeykey1", "{tag}exists").Result()
|
||||
fmt.Printf("\nval: %+v err: %+v\n", valm, err)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ go 1.18
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
go.uber.org/zap v1.24.0
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.18
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.17.0
|
||||
require github.com/redis/go-redis/v9 v9.17.1
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
|
||||
@@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.17.0
|
||||
require github.com/redis/go-redis/v9 v9.17.1
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
|
||||
@@ -11,8 +11,8 @@ replace github.com/redis/go-redis/extra/redisotel/v9 => ../../extra/redisotel
|
||||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../../extra/rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.17.1
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
github.com/uptrace/uptrace-go v1.21.0
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
)
|
||||
@@ -25,7 +25,7 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.17.0
|
||||
require github.com/redis/go-redis/v9 v9.17.1
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
|
||||
@@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.1
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
go.opencensus.io v0.24.0
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
require (
|
||||
github.com/bsm/ginkgo/v2 v2.12.0
|
||||
github.com/bsm/gomega v1.27.10
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -7,8 +7,8 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
replace github.com/redis/go-redis/extra/rediscmd/v9 => ../rediscmd
|
||||
|
||||
require (
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.17.1
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
go.opentelemetry.io/otel v1.22.0
|
||||
go.opentelemetry.io/otel/metric v1.22.0
|
||||
go.opentelemetry.io/otel/sdk v1.22.0
|
||||
|
||||
@@ -6,7 +6,7 @@ replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -2,5 +2,5 @@ package redis
|
||||
|
||||
// Version is the current release version.
|
||||
func Version() string {
|
||||
return "9.17.0"
|
||||
return "9.17.1"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user