mirror of
https://github.com/redis/go-redis.git
synced 2025-11-02 15:33:16 +03:00
chore(ci): Add redis 8.4-RC1-pre & examples (#3572)
* add disable maintnotifications example * add 8.4-RC1-pre * println -> printf for linter * address jit comment
This commit is contained in:
10
.github/actions/run-tests/action.yml
vendored
10
.github/actions/run-tests/action.yml
vendored
@@ -18,22 +18,20 @@ runs:
|
||||
- name: Setup Test environment
|
||||
env:
|
||||
REDIS_VERSION: ${{ inputs.redis-version }}
|
||||
CLIENT_LIBS_TEST_IMAGE: "redislabs/client-libs-test:${{ inputs.redis-version }}"
|
||||
run: |
|
||||
set -e
|
||||
redis_version_np=$(echo "$REDIS_VERSION" | grep -oP '^\d+.\d+')
|
||||
|
||||
|
||||
# Mapping of redis version to redis testing containers
|
||||
declare -A redis_version_mapping=(
|
||||
["8.4.x"]="8.4-RC1-pre"
|
||||
["8.2.x"]="8.2.1-pre"
|
||||
["8.0.x"]="8.0.2"
|
||||
["7.4.x"]="rs-7.4.0-v5"
|
||||
["7.2.x"]="rs-7.2.0-v17"
|
||||
)
|
||||
|
||||
|
||||
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then
|
||||
echo "REDIS_VERSION=${redis_version_np}" >> $GITHUB_ENV
|
||||
echo "REDIS_IMAGE=redis:${{ inputs.redis-version }}" >> $GITHUB_ENV
|
||||
echo "REDIS_IMAGE=redis:${REDIS_VERSION}" >> $GITHUB_ENV
|
||||
echo "CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:${redis_version_mapping[$REDIS_VERSION]}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Version not found in the mapping."
|
||||
|
||||
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -2,7 +2,7 @@ name: Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, v9, v9.7, v9.8, 'ndyakov/*', 'ofekshenawa/*', 'htemelski-redis/*', 'ce/*']
|
||||
branches: [master, v9, 'v9.*']
|
||||
pull_request:
|
||||
branches: [master, v9, v9.7, v9.8, 'ndyakov/*', 'ofekshenawa/*', 'htemelski-redis/*', 'ce/*']
|
||||
|
||||
@@ -18,9 +18,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
redis-version:
|
||||
- "8.4.x" # Redis CE 8.4
|
||||
- "8.2.x" # Redis CE 8.2
|
||||
- "8.0.x" # Redis CE 8.0
|
||||
- "7.4.x" # Redis stack 7.4
|
||||
go-version:
|
||||
- "1.23.x"
|
||||
- "1.24.x"
|
||||
@@ -44,9 +44,9 @@ jobs:
|
||||
|
||||
# Mapping of redis version to redis testing containers
|
||||
declare -A redis_version_mapping=(
|
||||
["8.4.x"]="8.4-RC1-pre"
|
||||
["8.2.x"]="8.2.1-pre"
|
||||
["8.0.x"]="8.0.2"
|
||||
["7.4.x"]="rs-7.4.0-v5"
|
||||
)
|
||||
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then
|
||||
echo "REDIS_VERSION=${redis_version_np}" >> $GITHUB_ENV
|
||||
@@ -74,10 +74,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
redis-version:
|
||||
- "8.4.x" # Redis CE 8.4
|
||||
- "8.2.x" # Redis CE 8.2
|
||||
- "8.0.x" # Redis CE 8.0
|
||||
- "7.4.x" # Redis stack 7.4
|
||||
- "7.2.x" # Redis stack 7.2
|
||||
go-version:
|
||||
- "1.23.x"
|
||||
- "1.24.x"
|
||||
|
||||
4
Makefile
4
Makefile
@@ -1,8 +1,8 @@
|
||||
GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
|
||||
REDIS_VERSION ?= 8.2
|
||||
REDIS_VERSION ?= 8.4
|
||||
RE_CLUSTER ?= false
|
||||
RCE_DOCKER ?= true
|
||||
CLIENT_LIBS_TEST_IMAGE ?= redislabs/client-libs-test:8.2.1-pre
|
||||
CLIENT_LIBS_TEST_IMAGE ?= redislabs/client-libs-test:8.4-RC1-pre
|
||||
|
||||
docker.start:
|
||||
export RE_CLUSTER=$(RE_CLUSTER) && \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4-RC1-pre}
|
||||
platform: linux/amd64
|
||||
container_name: redis-standalone
|
||||
environment:
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- all
|
||||
|
||||
osscluster:
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4-RC1-pre}
|
||||
platform: linux/amd64
|
||||
container_name: redis-osscluster
|
||||
environment:
|
||||
@@ -40,7 +40,7 @@ services:
|
||||
- all
|
||||
|
||||
sentinel-cluster:
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4-RC1-pre}
|
||||
platform: linux/amd64
|
||||
container_name: redis-sentinel-cluster
|
||||
network_mode: "host"
|
||||
@@ -60,7 +60,7 @@ services:
|
||||
- all
|
||||
|
||||
sentinel:
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4-RC1-pre}
|
||||
platform: linux/amd64
|
||||
container_name: redis-sentinel
|
||||
depends_on:
|
||||
@@ -84,7 +84,7 @@ services:
|
||||
- all
|
||||
|
||||
ring-cluster:
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
|
||||
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.4-RC1-pre}
|
||||
platform: linux/amd64
|
||||
container_name: redis-ring-cluster
|
||||
environment:
|
||||
|
||||
133
example/disable-maintnotifications/README.md
Normal file
133
example/disable-maintnotifications/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Disable Maintenance Notifications Example
|
||||
|
||||
This example demonstrates how to use the go-redis client with maintenance notifications **disabled**.
|
||||
|
||||
## What are Maintenance Notifications?
|
||||
|
||||
Maintenance notifications are a Redis Cloud feature that allows the server to notify clients about:
|
||||
- Planned maintenance events
|
||||
- Failover operations
|
||||
- Node migrations
|
||||
- Cluster topology changes
|
||||
|
||||
The go-redis client supports three modes:
|
||||
- **`ModeDisabled`**: Client doesn't send `CLIENT MAINT_NOTIFICATIONS ON` command
|
||||
- **`ModeEnabled`**: Client forcefully sends the command, interrupts connection on error
|
||||
- **`ModeAuto`** (default): Client tries to send the command, disables feature on error
|
||||
|
||||
## When to Disable Maintenance Notifications
|
||||
|
||||
You should disable maintenance notifications when:
|
||||
|
||||
1. **Connecting to non-Redis Cloud / Redis Enterprise instances** - Standard Redis servers don't support this feature
|
||||
2. **You want to handle failovers manually** - Your application has custom failover logic
|
||||
3. **Minimizing client-side overhead** - You want the simplest possible client behavior
|
||||
4. **The Redis server doesn't support the feature** - Older Redis versions or forks
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/redis/go-redis/v9/maintnotifications"
|
||||
)
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
|
||||
// Explicitly disable maintenance notifications
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeDisabled,
|
||||
},
|
||||
})
|
||||
defer rdb.Close()
|
||||
```
|
||||
|
||||
### Cluster Client Example
|
||||
|
||||
```go
|
||||
rdbCluster := redis.NewClusterClient(&redis.ClusterOptions{
|
||||
Addrs: []string{"localhost:7000", "localhost:7001", "localhost:7002"},
|
||||
|
||||
// Disable maintenance notifications for cluster
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeDisabled,
|
||||
},
|
||||
})
|
||||
defer rdbCluster.Close()
|
||||
```
|
||||
|
||||
### Default Behavior (ModeAuto)
|
||||
|
||||
If you don't specify `MaintNotifications`, the client defaults to `ModeAuto`:
|
||||
|
||||
```go
|
||||
// This uses ModeAuto by default
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
// MaintNotificationsConfig: nil means ModeAuto
|
||||
})
|
||||
```
|
||||
|
||||
With `ModeAuto`, the client will:
|
||||
1. Try to enable maintenance notifications
|
||||
2. If the server doesn't support it, silently disable the feature
|
||||
3. Continue normal operation
|
||||
|
||||
## Running the Example
|
||||
|
||||
1. Start a Redis server:
|
||||
```bash
|
||||
redis-server --port 6379
|
||||
```
|
||||
|
||||
2. Run the example:
|
||||
```bash
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## Expected Output
|
||||
|
||||
```
|
||||
=== Example 1: Explicitly Disabled ===
|
||||
✓ Connected successfully (maintenance notifications disabled)
|
||||
✓ SET operation successful
|
||||
✓ GET operation successful: value1
|
||||
|
||||
=== Example 2: Default Behavior (ModeAuto) ===
|
||||
✓ Connected successfully (maintenance notifications auto-enabled)
|
||||
|
||||
=== Example 3: Cluster Client with Disabled Notifications ===
|
||||
Cluster not available (expected): ...
|
||||
|
||||
=== Example 4: Performance Comparison ===
|
||||
✓ 1000 SET operations (disabled): 45ms
|
||||
✓ 1000 SET operations (auto): 46ms
|
||||
|
||||
=== Cleanup ===
|
||||
✓ Database flushed
|
||||
|
||||
=== Summary ===
|
||||
Maintenance notifications can be disabled by setting:
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeDisabled,
|
||||
}
|
||||
|
||||
This is useful when:
|
||||
- Connecting to non-Redis Cloud instances
|
||||
- You want to handle failovers manually
|
||||
- You want to minimize client-side overhead
|
||||
- The Redis server doesn't support CLIENT MAINT_NOTIFICATIONS
|
||||
```
|
||||
|
||||
## Performance Impact
|
||||
|
||||
Disabling maintenance notifications has minimal performance impact. The main differences are:
|
||||
|
||||
1. **Connection Setup**: One less command (`CLIENT MAINT_NOTIFICATIONS ON`) during connection initialization
|
||||
2. **Runtime Overhead**: No background processing of maintenance notifications
|
||||
3. **Memory Usage**: Slightly lower memory footprint (no notification handlers)
|
||||
|
||||
In most cases, the performance difference is negligible (< 1%).
|
||||
12
example/disable-maintnotifications/go.mod
Normal file
12
example/disable-maintnotifications/go.mod
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/redis/go-redis/example/disable-maintnotifications
|
||||
|
||||
go 1.23
|
||||
|
||||
replace github.com/redis/go-redis/v9 => ../..
|
||||
|
||||
require github.com/redis/go-redis/v9 v9.7.0
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
)
|
||||
8
example/disable-maintnotifications/go.sum
Normal file
8
example/disable-maintnotifications/go.sum
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
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=
|
||||
144
example/disable-maintnotifications/main.go
Normal file
144
example/disable-maintnotifications/main.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/redis/go-redis/v9/maintnotifications"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Example 0: Explicitly disable maintenance notifications
|
||||
fmt.Println("=== Example 0: Explicitly Enabled ===")
|
||||
rdb0 := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
|
||||
// Explicitly disable maintenance notifications
|
||||
// This prevents the client from sending CLIENT MAINT_NOTIFICATIONS ON
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeEnabled,
|
||||
},
|
||||
})
|
||||
defer rdb0.Close()
|
||||
|
||||
// Test the connection
|
||||
if err := rdb0.Ping(ctx).Err(); err != nil {
|
||||
fmt.Printf("Failed to connect: %v\n\n", err)
|
||||
}
|
||||
fmt.Println("When ModeEnabled, the client will return an error if the server doesn't support maintenance notifications.")
|
||||
fmt.Printf("ModeAuto will silently disable the feature.\n\n")
|
||||
|
||||
// Example 1: Explicitly disable maintenance notifications
|
||||
fmt.Println("=== Example 1: Explicitly Disabled ===")
|
||||
rdb1 := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
|
||||
// Explicitly disable maintenance notifications
|
||||
// This prevents the client from sending CLIENT MAINT_NOTIFICATIONS ON
|
||||
MaintNotificationsConfig: &maintnotifications.Config{
|
||||
Mode: maintnotifications.ModeDisabled,
|
||||
},
|
||||
})
|
||||
defer rdb1.Close()
|
||||
|
||||
// Test the connection
|
||||
if err := rdb1.Ping(ctx).Err(); err != nil {
|
||||
fmt.Printf("Failed to connect: %v\n\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("✓ Connected successfully (maintenance notifications disabled)")
|
||||
|
||||
// Perform some operations
|
||||
if err := rdb1.Set(ctx, "example:key1", "value1", 0).Err(); err != nil {
|
||||
fmt.Printf("Failed to set key: %v\n\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("✓ SET operation successful")
|
||||
|
||||
val, err := rdb1.Get(ctx, "example:key1").Result()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get key: %v\n\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("✓ GET operation successful: %s\n\n", val)
|
||||
|
||||
// Example 2: Using nil config (defaults to ModeAuto)
|
||||
fmt.Printf("\n=== Example 2: Default Behavior (ModeAuto) ===\n")
|
||||
rdb2 := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
// MaintNotifications: nil means ModeAuto (enabled for Redis Cloud)
|
||||
})
|
||||
defer rdb2.Close()
|
||||
|
||||
if err := rdb2.Ping(ctx).Err(); err != nil {
|
||||
fmt.Printf("Failed to connect: %v\n\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("✓ Connected successfully (maintenance notifications auto-enabled)")
|
||||
|
||||
// Example 4: Comparing behavior with and without maintenance notifications
|
||||
fmt.Printf("\n=== Example 4: Performance Comparison ===\n")
|
||||
|
||||
// Client with auto-enabled notifications
|
||||
startauto := time.Now()
|
||||
for i := 0; i < 1000; i++ {
|
||||
key := fmt.Sprintf("test:auto:%d", i)
|
||||
if err := rdb2.Set(ctx, key, i, time.Minute).Err(); err != nil {
|
||||
fmt.Printf("Failed to set key: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
autoDuration := time.Since(startauto)
|
||||
fmt.Printf("✓ 1000 SET operations (auto): %v\n", autoDuration)
|
||||
|
||||
// print pool stats
|
||||
fmt.Printf("Pool stats (auto): %+v\n", rdb2.PoolStats())
|
||||
|
||||
// give the server a moment to take chill
|
||||
fmt.Println("---")
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Client with disabled notifications
|
||||
start := time.Now()
|
||||
for i := 0; i < 1000; i++ {
|
||||
key := fmt.Sprintf("test:disabled:%d", i)
|
||||
if err := rdb1.Set(ctx, key, i, time.Minute).Err(); err != nil {
|
||||
fmt.Printf("Failed to set key: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
disabledDuration := time.Since(start)
|
||||
fmt.Printf("✓ 1000 SET operations (disabled): %v\n", disabledDuration)
|
||||
fmt.Printf("Pool stats (disabled): %+v\n", rdb1.PoolStats())
|
||||
|
||||
// performance comparison note
|
||||
fmt.Printf("\nNote: The pool stats and performance are identical because there is no background processing overhead.\n")
|
||||
fmt.Println("Since the server doesn't support maintenance notifications, there is no difference in behavior.")
|
||||
fmt.Printf("The only difference is that the \"ModeDisabled\" client doesn't send the CLIENT MAINT_NOTIFICATIONS ON command.\n\n")
|
||||
fmt.Println("p.s. reordering the execution here makes it look like there is a small performance difference, but it's just noise.")
|
||||
|
||||
// Cleanup
|
||||
fmt.Printf("\n=== Cleanup ===\n")
|
||||
if err := rdb1.FlushDB(ctx).Err(); err != nil {
|
||||
fmt.Printf("Failed to flush DB: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("✓ Database flushed")
|
||||
|
||||
fmt.Printf("\n=== Summary ===\n")
|
||||
fmt.Println("Maintenance notifications can be disabled by setting:")
|
||||
fmt.Println(" MaintNotifications: &maintnotifications.Config{")
|
||||
fmt.Println(" Mode: maintnotifications.ModeDisabled,")
|
||||
fmt.Println(" }")
|
||||
fmt.Printf("\nThis is useful when:\n")
|
||||
fmt.Println(" - Connecting to non-Redis Cloud instances")
|
||||
fmt.Println(" - You want to handle failovers manually")
|
||||
fmt.Println(" - You want to minimize client-side overhead")
|
||||
fmt.Println(" - The Redis server doesn't support CLIENT MAINT_NOTIFICATIONS")
|
||||
fmt.Printf("\nFor more information, see:\n")
|
||||
fmt.Println(" https://github.com/redis/go-redis/tree/master/maintnotifications")
|
||||
}
|
||||
@@ -69,7 +69,7 @@ var RCEDocker = false
|
||||
// Notes version of redis we are executing tests against.
|
||||
// This can be used before we change the bsm fork of ginkgo for one,
|
||||
// which have support for label sets, so we can filter tests per redis version.
|
||||
var RedisVersion float64 = 8.2
|
||||
var RedisVersion float64 = 8.4
|
||||
|
||||
func SkipBeforeRedisVersion(version float64, msg string) {
|
||||
if RedisVersion < version {
|
||||
@@ -96,7 +96,7 @@ var _ = BeforeSuite(func() {
|
||||
RedisVersion, _ = strconv.ParseFloat(strings.Trim(os.Getenv("REDIS_VERSION"), "\""), 64)
|
||||
|
||||
if RedisVersion == 0 {
|
||||
RedisVersion = 8.2
|
||||
RedisVersion = 8.4
|
||||
}
|
||||
|
||||
fmt.Printf("RECluster: %v\n", RECluster)
|
||||
|
||||
Reference in New Issue
Block a user