diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 0f1112f8..f9770186 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,64 @@ # Release Notes +# 9.16.0 (2025-10-23) + +## 🚀 Highlights + +### Maintenance Notifications Support + +This release introduces comprehensive support for Redis maintenance notifications, enabling applications to handle server maintenance events gracefully. The new `maintnotifications` package provides: + +- **RESP3 Push Notifications**: Full support for Redis RESP3 protocol push notifications +- **Connection Handoff**: Automatic connection migration during server maintenance with configurable retry policies and circuit breakers +- **Graceful Degradation**: Configurable timeout relaxation during maintenance windows to prevent false failures +- **Event-Driven Architecture**: Background workers with on-demand scaling for efficient handoff processing +- **Production-Ready**: Comprehensive E2E testing framework and monitoring capabilities + +For detailed usage examples and configuration options, see the [maintenance notifications documentation](maintnotifications/README.md). + +## ✨ New Features + +- **Trace Filtering**: Add support for filtering traces for specific commands, including pipeline operations and dial operations ([#3519](https://github.com/redis/go-redis/pull/3519), [#3550](https://github.com/redis/go-redis/pull/3550)) + - New `TraceCmdFilter` option to selectively trace commands + - Reduces overhead by excluding high-frequency or low-value commands from traces + +## 🐛 Bug Fixes + +- **Pipeline Error Handling**: Fix issue where pipeline repeatedly sets the same error ([#3525](https://github.com/redis/go-redis/pull/3525)) +- **Connection Pool**: Ensure re-authentication does not interfere with connection handoff operations ([#3547](https://github.com/redis/go-redis/pull/3547)) + +## 🔧 Improvements + +- **Hash Commands**: Update hash command implementations ([#3523](https://github.com/redis/go-redis/pull/3523)) +- **OpenTelemetry**: Use `metric.WithAttributeSet` to avoid unnecessary attribute copying in redisotel ([#3552](https://github.com/redis/go-redis/pull/3552)) + +## 📚 Documentation + +- **Cluster Client**: Add explanation for why `MaxRetries` is disabled for `ClusterClient` ([#3551](https://github.com/redis/go-redis/pull/3551)) + +## 🧪 Testing & Infrastructure + +- **E2E Testing**: Upgrade E2E testing framework with improved reliability and coverage ([#3541](https://github.com/redis/go-redis/pull/3541)) +- **Release Process**: Improved resiliency of the release process ([#3530](https://github.com/redis/go-redis/pull/3530)) + +## 📦 Dependencies + +- Bump `rojopolis/spellcheck-github-actions` from 0.51.0 to 0.52.0 ([#3520](https://github.com/redis/go-redis/pull/3520)) +- Bump `github/codeql-action` from 3 to 4 ([#3544](https://github.com/redis/go-redis/pull/3544)) + +## 👥 Contributors + +We'd like to thank all the contributors who worked on this release! + +[@ndyakov](https://github.com/ndyakov), [@htemelski-redis](https://github.com/htemelski-redis), [@Sovietaced](https://github.com/Sovietaced), [@Udhayarajan](https://github.com/Udhayarajan), [@boekkooi-impossiblecloud](https://github.com/boekkooi-impossiblecloud), [@Pika-Gopher](https://github.com/Pika-Gopher), [@cxljs](https://github.com/cxljs), [@huiyifyj](https://github.com/huiyifyj), [@omid-h70](https://github.com/omid-h70) + +--- + +**Full Changelog**: https://github.com/redis/go-redis/compare/v9.14.0...v9.16.0 + + +# 9.15.0 was accidentally released. Please use version 9.16.0 instead. + # 9.15.0-beta.3 (2025-09-26) ## Highlights diff --git a/example/del-keys-without-ttl/go.mod b/example/del-keys-without-ttl/go.mod index 6891389d..d808c723 100644 --- a/example/del-keys-without-ttl/go.mod +++ b/example/del-keys-without-ttl/go.mod @@ -5,7 +5,7 @@ go 1.18 replace github.com/redis/go-redis/v9 => ../.. require ( - github.com/redis/go-redis/v9 v9.16.0-beta.1 + github.com/redis/go-redis/v9 v9.16.0 go.uber.org/zap v1.24.0 ) diff --git a/example/hll/go.mod b/example/hll/go.mod index b10cc17e..54178bf2 100644 --- a/example/hll/go.mod +++ b/example/hll/go.mod @@ -4,7 +4,7 @@ go 1.18 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.16.0-beta.1 +require github.com/redis/go-redis/v9 v9.16.0 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/hset-struct/go.mod b/example/hset-struct/go.mod index 9c466d1c..81c7c3dd 100644 --- a/example/hset-struct/go.mod +++ b/example/hset-struct/go.mod @@ -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.16.0-beta.1 + github.com/redis/go-redis/v9 v9.16.0 ) require ( diff --git a/example/lua-scripting/go.mod b/example/lua-scripting/go.mod index 24c92753..02023f7b 100644 --- a/example/lua-scripting/go.mod +++ b/example/lua-scripting/go.mod @@ -4,7 +4,7 @@ go 1.18 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.16.0-beta.1 +require github.com/redis/go-redis/v9 v9.16.0 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/otel/go.mod b/example/otel/go.mod index 93a0fbf1..3883f546 100644 --- a/example/otel/go.mod +++ b/example/otel/go.mod @@ -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.16.0-beta.1 - github.com/redis/go-redis/v9 v9.16.0-beta.1 + github.com/redis/go-redis/extra/redisotel/v9 v9.16.0 + github.com/redis/go-redis/v9 v9.16.0 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.16.0-beta.1 // indirect + github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 // 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 diff --git a/example/redis-bloom/go.mod b/example/redis-bloom/go.mod index 49b34be8..f25b4b24 100644 --- a/example/redis-bloom/go.mod +++ b/example/redis-bloom/go.mod @@ -4,7 +4,7 @@ go 1.18 replace github.com/redis/go-redis/v9 => ../.. -require github.com/redis/go-redis/v9 v9.16.0-beta.1 +require github.com/redis/go-redis/v9 v9.16.0 require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/example/scan-struct/go.mod b/example/scan-struct/go.mod index 9c466d1c..81c7c3dd 100644 --- a/example/scan-struct/go.mod +++ b/example/scan-struct/go.mod @@ -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.16.0-beta.1 + github.com/redis/go-redis/v9 v9.16.0 ) require ( diff --git a/extra/rediscensus/go.mod b/extra/rediscensus/go.mod index 00324f22..d4272c97 100644 --- a/extra/rediscensus/go.mod +++ b/extra/rediscensus/go.mod @@ -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.16.0-beta.1 - github.com/redis/go-redis/v9 v9.16.0-beta.1 + github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 + github.com/redis/go-redis/v9 v9.16.0 go.opencensus.io v0.24.0 ) diff --git a/extra/rediscmd/go.mod b/extra/rediscmd/go.mod index 03b8c498..d8c03b6a 100644 --- a/extra/rediscmd/go.mod +++ b/extra/rediscmd/go.mod @@ -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.16.0-beta.1 + github.com/redis/go-redis/v9 v9.16.0 ) require ( diff --git a/extra/redisotel/go.mod b/extra/redisotel/go.mod index 89792749..23cec11a 100644 --- a/extra/redisotel/go.mod +++ b/extra/redisotel/go.mod @@ -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.16.0-beta.1 - github.com/redis/go-redis/v9 v9.16.0-beta.1 + github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 + github.com/redis/go-redis/v9 v9.16.0 go.opentelemetry.io/otel v1.22.0 go.opentelemetry.io/otel/metric v1.22.0 go.opentelemetry.io/otel/sdk v1.22.0 diff --git a/extra/redisprometheus/go.mod b/extra/redisprometheus/go.mod index 8a32cde6..fd4e2d93 100644 --- a/extra/redisprometheus/go.mod +++ b/extra/redisprometheus/go.mod @@ -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.16.0-beta.1 + github.com/redis/go-redis/v9 v9.16.0 ) require ( diff --git a/maintnotifications/FEATURES.md b/maintnotifications/FEATURES.md new file mode 100644 index 00000000..caa4f705 --- /dev/null +++ b/maintnotifications/FEATURES.md @@ -0,0 +1,218 @@ +# Maintenance Notifications - FEATURES + +## Overview + +The Maintenance Notifications feature enables seamless Redis connection handoffs during cluster maintenance operations without dropping active connections. This feature leverages Redis RESP3 push notifications to provide zero-downtime maintenance for Redis Enterprise and compatible Redis deployments. + +## Important + +Using Maintenance Notifications may affect the read and write timeouts by relaxing them during maintenance operations. +This is necessary to prevent false failures due to increased latency during handoffs. The relaxed timeouts are automatically applied and removed as needed. + +## Key Features + +### Seamless Connection Handoffs +- **Zero-Downtime Maintenance**: Automatically handles connection transitions during cluster operations +- **Active Operation Preservation**: Transfers in-flight operations to new connections without interruption +- **Graceful Degradation**: Falls back to standard reconnection if handoff fails + +### Push Notification Support +Supports all Redis Enterprise maintenance notification types: +- **MOVING** - Slot moving to a new node +- **MIGRATING** - Slot in migration state +- **MIGRATED** - Migration completed +- **FAILING_OVER** - Node failing over +- **FAILED_OVER** - Failover completed + +### Circuit Breaker Pattern +- **Endpoint-Specific Failure Tracking**: Prevents repeated connection attempts to failing endpoints +- **Automatic Recovery Testing**: Half-open state allows gradual recovery validation +- **Configurable Thresholds**: Customize failure thresholds and reset timeouts + +### Flexible Configuration +- **Auto-Detection Mode**: Automatically detects server support for maintenance notifications +- **Multiple Endpoint Types**: Support for internal/external IP/FQDN endpoint resolution +- **Auto-Scaling Workers**: Automatically sizes worker pool based on connection pool size +- **Timeout Management**: Separate timeouts for relaxed (during maintenance) and normal operations + +### Extensible Hook System +- **Pre/Post Processing Hooks**: Monitor and customize notification handling +- **Built-in Hooks**: Logging and metrics collection hooks included +- **Custom Hook Support**: Implement custom business logic around maintenance events + +### Comprehensive Monitoring +- **Metrics Collection**: Track notification counts, processing times, and error rates +- **Circuit Breaker Stats**: Monitor endpoint health and circuit breaker states +- **Operation Tracking**: Track active handoff operations and their lifecycle + +## Architecture Highlights + +### Event-Driven Handoff System +- **Asynchronous Processing**: Non-blocking handoff operations using worker pool pattern +- **Queue-Based Architecture**: Configurable queue size with auto-scaling support +- **Retry Mechanism**: Configurable retry attempts with exponential backoff + +### Connection Pool Integration +- **Pool Hook Interface**: Seamless integration with go-redis connection pool +- **Connection State Management**: Atomic flags for connection usability tracking +- **Graceful Shutdown**: Ensures all in-flight handoffs complete before shutdown + +### Thread-Safe Design +- **Lock-Free Operations**: Atomic operations for high-performance state tracking +- **Concurrent-Safe Maps**: sync.Map for tracking active operations +- **Minimal Lock Contention**: Read-write locks only where necessary + +## Configuration Options + +### Operation Modes +- **`ModeDisabled`**: Maintenance notifications completely disabled +- **`ModeEnabled`**: Forcefully enabled (fails if server doesn't support) +- **`ModeAuto`**: Auto-detect server support (recommended default) + +### Endpoint Types +- **`EndpointTypeAuto`**: Auto-detect based on current connection +- **`EndpointTypeInternalIP`**: Use internal IP addresses +- **`EndpointTypeInternalFQDN`**: Use internal fully qualified domain names +- **`EndpointTypeExternalIP`**: Use external IP addresses +- **`EndpointTypeExternalFQDN`**: Use external fully qualified domain names +- **`EndpointTypeNone`**: No endpoint (reconnect with current configuration) + +### Timeout Configuration +- **`RelaxedTimeout`**: Extended timeout during maintenance operations (default: 10s) +- **`HandoffTimeout`**: Maximum time for handoff completion (default: 15s) +- **`PostHandoffRelaxedDuration`**: Relaxed period after handoff (default: 2×RelaxedTimeout) + +### Worker Pool Configuration +- **`MaxWorkers`**: Maximum concurrent handoff workers (auto-calculated if 0) +- **`HandoffQueueSize`**: Handoff queue capacity (auto-calculated if 0) +- **`MaxHandoffRetries`**: Maximum retry attempts for failed handoffs (default: 3) + +### Circuit Breaker Configuration +- **`CircuitBreakerFailureThreshold`**: Failures before opening circuit (default: 5) +- **`CircuitBreakerResetTimeout`**: Time before testing recovery (default: 60s) +- **`CircuitBreakerMaxRequests`**: Max requests in half-open state (default: 3) + +## Auto-Scaling Formulas + +### Worker Pool Sizing +When `MaxWorkers = 0` (auto-calculate): +``` +MaxWorkers = min(PoolSize/2, max(10, PoolSize/3)) +``` + +### Queue Sizing +When `HandoffQueueSize = 0` (auto-calculate): +``` +QueueSize = max(20 × MaxWorkers, PoolSize) +Capped by: min(MaxActiveConns + 1, 5 × PoolSize) +``` + +### Examples +- **Pool Size 100**: 33 workers, 660 queue (capped at 500) +- **Pool Size 100 + MaxActiveConns 150**: 33 workers, 151 queue +- **Pool Size 50**: 16 workers, 320 queue (capped at 250) + +## Performance Characteristics + +### Throughput +- **Non-Blocking Handoffs**: Client operations continue during handoffs +- **Concurrent Processing**: Multiple handoffs processed in parallel +- **Minimal Overhead**: Lock-free atomic operations for state tracking + +### Latency +- **Relaxed Timeouts**: Extended timeouts during maintenance prevent false failures +- **Fast Path**: Connections not undergoing handoff have zero overhead +- **Graceful Degradation**: Failed handoffs fall back to standard reconnection + +### Resource Usage +- **Memory Efficient**: Bounded queue sizes prevent memory exhaustion +- **Worker Pool**: Fixed worker count prevents goroutine explosion +- **Connection Reuse**: Handoff reuses existing connection objects + +## Testing + +### Unit Tests +- Comprehensive unit test coverage for all components +- Mock-based testing for isolation +- Concurrent operation testing + +### Integration Tests +- Pool integration tests with real connection handoffs +- Circuit breaker behavior validation +- Hook system integration testing + +### E2E Tests +- Real Redis Enterprise cluster testing +- Multiple scenario coverage (timeouts, endpoint types, stress tests) +- Fault injection testing +- TLS configuration testing + +## Compatibility + +### Requirements +- **Redis Protocol**: RESP3 required for push notifications +- **Redis Version**: Redis Enterprise or compatible Redis with maintenance notifications +- **Go Version**: Go 1.18+ (uses generics and atomic types) + +### Client Support +#### Currently Supported +- **Standalone Client** (`redis.NewClient`) + +#### Planned Support +- **Cluster Client** (not yet supported) + +#### Will Not Support +- **Failover Client** (no planned support) +- **Ring Client** (no planned support) + +## Migration Guide + +### Enabling Maintenance Notifications + +**Before:** +```go +client := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Protocol: 2, // RESP2 +}) +``` + +**After:** +```go +client := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Protocol: 3, // RESP3 required + MaintNotificationsConfig: &maintnotifications.Config{ + Mode: maintnotifications.ModeAuto, + }, +}) +``` + +### Adding Monitoring + +```go +// Get the manager from the client +manager := client.GetMaintNotificationsManager() +if manager != nil { + // Add logging hook + loggingHook := maintnotifications.NewLoggingHook(2) // Info level + manager.AddNotificationHook(loggingHook) + + // Add metrics hook + metricsHook := maintnotifications.NewMetricsHook() + manager.AddNotificationHook(metricsHook) +} +``` + +## Known Limitations + +1. **Standalone Only**: Currently only supported in standalone Redis clients +2. **RESP3 Required**: Push notifications require RESP3 protocol +3. **Server Support**: Requires Redis Enterprise or compatible Redis with maintenance notifications +4. **Single Connection Commands**: Some commands (MULTI/EXEC, WATCH) may need special handling +5. **No Failover/Ring Client Support**: Failover and Ring clients are not supported and there are no plans to add support + +## Future Enhancements + +- Cluster client support +- Enhanced metrics and observability \ No newline at end of file diff --git a/maintnotifications/README.md b/maintnotifications/README.md index 33b737f6..2ac6b9cb 100644 --- a/maintnotifications/README.md +++ b/maintnotifications/README.md @@ -63,38 +63,5 @@ client := redis.NewClient(&redis.Options{ 3. Active operations transfer to new connections 4. Old connections close gracefully -## Supported Notifications -- `MOVING` - Slot moving to new node -- `MIGRATING` - Slot in migration state -- `MIGRATED` - Migration completed -- `FAILING_OVER` - Node failing over -- `FAILED_OVER` - Failover completed - -## Hooks (Optional) - -Monitor and customize maintenance notification operations: - -```go -type NotificationHook interface { - PreHook(ctx, notificationCtx, notificationType, notification) ([]interface{}, bool) - PostHook(ctx, notificationCtx, notificationType, notification, result) -} - -// Add custom hook -manager.AddNotificationHook(&MyHook{}) -``` - -### Metrics Hook Example - -```go -// Create metrics hook -metricsHook := maintnotifications.NewMetricsHook() -manager.AddNotificationHook(metricsHook) - -// Access collected metrics -metrics := metricsHook.GetMetrics() -fmt.Printf("Notification counts: %v\n", metrics["notification_counts"]) -fmt.Printf("Processing times: %v\n", metrics["processing_times"]) -fmt.Printf("Error counts: %v\n", metrics["error_counts"]) -``` +## For more information, see [FEATURES](FEATURES.md) diff --git a/version.go b/version.go index e04248a8..462c19e2 100644 --- a/version.go +++ b/version.go @@ -2,5 +2,5 @@ package redis // Version is the current release version. func Version() string { - return "9.16.0-beta.1" + return "9.16.0" }