Skip to content

Commit 18150e4

Browse files
committed
log level as const
1 parent 418c1f3 commit 18150e4

File tree

12 files changed

+129
-49
lines changed

12 files changed

+129
-49
lines changed

example/pubsub/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/redis/go-redis/v9"
1212
"github.com/redis/go-redis/v9/hitless"
13+
"github.com/redis/go-redis/v9/logging"
1314
)
1415

1516
var ctx = context.Background()
@@ -34,7 +35,7 @@ func main() {
3435
if hitlessManager == nil {
3536
panic("hitless manager is nil")
3637
}
37-
loggingHook := hitless.NewLoggingHook(3)
38+
loggingHook := hitless.NewLoggingHook(logging.LogLevelDebug)
3839
hitlessManager.AddNotificationHook(loggingHook)
3940

4041
go func() {

hitless/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,18 @@ client := redis.NewClient(opt)
2626
## Configuration
2727

2828
```go
29-
import "github.com/redis/go-redis/v9/hitless"
29+
import (
30+
"github.com/redis/go-redis/v9/hitless"
31+
"github.com/redis/go-redis/v9/logging"
32+
)
3033

3134
Config: &hitless.Config{
3235
Mode: hitless.MaintNotificationsAuto, // Notification mode
3336
MaxHandoffRetries: 3, // Retry failed handoffs
3437
HandoffTimeout: 15 * time.Second, // Handoff operation timeout
3538
RelaxedTimeout: 10 * time.Second, // Extended timeout during migrations
3639
PostHandoffRelaxedDuration: 20 * time.Second, // Keep relaxed timeout after handoff
37-
LogLevel: 1, // 0=errors, 1=warnings, 2=info, 3=debug
40+
LogLevel: logging.LogLevelWarn, // LogLevelError, LogLevelWarn, LogLevelInfo, LogLevelDebug
3841
MaxWorkers: 15, // Concurrent handoff workers
3942
HandoffQueueSize: 50, // Handoff request queue size
4043
}

hitless/config.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ import (
99

1010
"github.com/redis/go-redis/v9/internal"
1111
"github.com/redis/go-redis/v9/internal/util"
12+
"github.com/redis/go-redis/v9/logging"
13+
)
14+
15+
// LogLevel represents the logging level for hitless upgrades
16+
type LogLevel = logging.LogLevel
17+
18+
// Log level constants for hitless upgrades
19+
const (
20+
LogLevelError LogLevel = logging.LogLevelError // 0 - errors only
21+
LogLevelWarn LogLevel = logging.LogLevelWarn // 1 - warnings and errors
22+
LogLevelInfo LogLevel = logging.LogLevelInfo // 2 - info, warnings, and errors
23+
LogLevelDebug LogLevel = logging.LogLevelDebug // 3 - debug, info, warnings, and errors
1224
)
1325

1426
// MaintNotificationsMode represents the maintenance notifications mode
@@ -116,9 +128,9 @@ type Config struct {
116128
ScaleDownDelay time.Duration
117129

118130
// LogLevel controls the verbosity of hitless upgrade logging.
119-
// 0 = errors only, 1 = warnings, 2 = info, 3 = debug
120-
// Default: 1 (warnings)
121-
LogLevel int
131+
// LogLevelError (0) = errors only, LogLevelWarn (1) = warnings, LogLevelInfo (2) = info, LogLevelDebug (3) = debug
132+
// Default: LogLevelWarn (warnings)
133+
LogLevel LogLevel
122134

123135
// MaxHandoffRetries is the maximum number of times to retry a failed handoff.
124136
// After this many retries, the connection will be removed from the pool.
@@ -141,7 +153,7 @@ func DefaultConfig() *Config {
141153
HandoffQueueSize: 0, // Auto-calculated based on max workers
142154
PostHandoffRelaxedDuration: 0, // Auto-calculated based on relaxed timeout
143155
ScaleDownDelay: 2 * time.Second,
144-
LogLevel: 1,
156+
LogLevel: LogLevelWarn,
145157

146158
// Connection Handoff Configuration
147159
MaxHandoffRetries: 3,
@@ -168,7 +180,7 @@ func (c *Config) Validate() error {
168180
if c.PostHandoffRelaxedDuration < 0 {
169181
return ErrInvalidPostHandoffRelaxedDuration
170182
}
171-
if c.LogLevel < 0 || c.LogLevel > 3 {
183+
if !c.LogLevel.IsValid() {
172184
return ErrInvalidLogLevel
173185
}
174186

@@ -281,8 +293,7 @@ func (c *Config) ApplyDefaultsWithPoolSize(poolSize int) *Config {
281293
result.MaxHandoffRetries = c.MaxHandoffRetries
282294
}
283295

284-
if result.LogLevel >= 3 {
285-
result.LogLevel = 3
296+
if result.LogLevel >= LogLevelDebug {
286297
internal.Logger.Printf(context.Background(), "hitless: debug logging enabled")
287298
internal.Logger.Printf(context.Background(), "hitless: config: %+v", result)
288299
}

hitless/config_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/redis/go-redis/v9/internal/util"
10+
"github.com/redis/go-redis/v9/logging"
1011
)
1112

1213
func TestConfig(t *testing.T) {
@@ -293,8 +294,8 @@ func TestIntegrationWithApplyDefaults(t *testing.T) {
293294
t.Run("ProcessorWithPartialConfigAppliesDefaults", func(t *testing.T) {
294295
// Create a partial config with only some fields set
295296
partialConfig := &Config{
296-
MaxWorkers: 15, // Custom value (>= 10 to test preservation)
297-
LogLevel: 2, // Custom value
297+
MaxWorkers: 15, // Custom value (>= 10 to test preservation)
298+
LogLevel: logging.LogLevelInfo, // Custom value
298299
// Other fields left as zero values - should get defaults
299300
}
300301

hitless/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var (
1111
ErrInvalidHandoffWorkers = errors.New("hitless: MaxWorkers must be greater than or equal to 0")
1212
ErrInvalidHandoffQueueSize = errors.New("hitless: handoff queue size must be greater than 0")
1313
ErrInvalidPostHandoffRelaxedDuration = errors.New("hitless: post-handoff relaxed duration must be greater than or equal to 0")
14-
ErrInvalidLogLevel = errors.New("hitless: log level must be between 0 and 3")
14+
ErrInvalidLogLevel = errors.New("hitless: log level must be LogLevelError (0), LogLevelWarn (1), LogLevelInfo (2), or LogLevelDebug (3)")
1515
ErrInvalidEndpointType = errors.New("hitless: invalid endpoint type")
1616
ErrInvalidMaintNotifications = errors.New("hitless: invalid maintenance notifications setting (must be 'disabled', 'enabled', or 'auto')")
1717
ErrMaxHandoffRetriesReached = errors.New("hitless: max handoff retries reached")

hitless/hitless_manager.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,14 @@ func (hm *HitlessManager) TrackMovingOperationWithConnID(ctx context.Context, ne
148148
// Use LoadOrStore for atomic check-and-set operation
149149
if _, loaded := hm.activeMovingOps.LoadOrStore(key, movingOp); loaded {
150150
// Duplicate MOVING notification, ignore
151-
if hm.config.LogLevel >= 3 { // Warning level
152-
internal.Logger.Printf(ctx, "hitless: conn[%d] seqID[%d] Duplicate MOVING operation ignored: %s", connID, seqID, key.String())
151+
if hm.config.LogLevel >= LogLevelDebug { // Debug level
152+
internal.Logger.Printf(context.Background(), "hitless: conn[%d] seqID[%d] Duplicate MOVING operation ignored: %s", connID, seqID, key.String())
153153
}
154154
return nil
155155
}
156+
if hm.config.LogLevel >= LogLevelDebug { // Debug level
157+
internal.Logger.Printf(context.Background(), "hitless: conn[%d] seqID[%d] Tracking MOVING operation: %s", connID, seqID, key.String())
158+
}
156159

157160
// Increment active operation count atomically
158161
hm.activeOperationCount.Add(1)
@@ -170,10 +173,16 @@ func (hm *HitlessManager) UntrackOperationWithConnID(seqID int64, connID uint64)
170173

171174
// Remove from active operations atomically
172175
if _, loaded := hm.activeMovingOps.LoadAndDelete(key); loaded {
176+
if hm.config.LogLevel >= LogLevelDebug { // Debug level
177+
internal.Logger.Printf(context.Background(), "hitless: conn[%d] seqID[%d] Untracking MOVING operation: %s", connID, seqID, key.String())
178+
}
173179
// Decrement active operation count only if operation existed
174180
hm.activeOperationCount.Add(-1)
175181
} else {
176-
if hm.config.LogLevel >= 3 { // Warning level
182+
for key, _ := range hm.GetActiveMovingOperations() {
183+
internal.Logger.Printf(context.Background(), "hitless: active op: key: %s", key.String())
184+
}
185+
if hm.config.LogLevel >= LogLevelDebug { // Debug level
177186
internal.Logger.Printf(context.Background(), "hitless: conn[%d] seqID[%d] Operation not found for untracking: %s", connID, seqID, key.String())
178187
}
179188
}

hitless/hooks.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import (
55

66
"github.com/redis/go-redis/v9/internal"
77
"github.com/redis/go-redis/v9/internal/pool"
8+
"github.com/redis/go-redis/v9/logging"
89
"github.com/redis/go-redis/v9/push"
910
)
1011

1112
// LoggingHook is an example hook implementation that logs all notifications.
1213
type LoggingHook struct {
13-
LogLevel int
14+
LogLevel logging.LogLevel
1415
}
1516

1617
// PreHook logs the notification before processing and allows modification.
1718
func (lh *LoggingHook) PreHook(ctx context.Context, notificationCtx push.NotificationHandlerContext, notificationType string, notification []interface{}) ([]interface{}, bool) {
18-
if lh.LogLevel >= 2 { // Info level
19+
if lh.LogLevel >= logging.LogLevelInfo { // Info level
1920
// Log the notification type and content
2021
connID := uint64(0)
2122
if conn, ok := notificationCtx.Conn.(*pool.Conn); ok {
@@ -32,15 +33,15 @@ func (lh *LoggingHook) PostHook(ctx context.Context, notificationCtx push.Notifi
3233
if conn, ok := notificationCtx.Conn.(*pool.Conn); ok {
3334
connID = conn.GetID()
3435
}
35-
if result != nil && lh.LogLevel >= 1 { // Warning level
36+
if result != nil && lh.LogLevel >= logging.LogLevelWarn { // Warning level
3637
internal.Logger.Printf(ctx, "hitless: conn[%d] %s notification processing failed: %v - %v", connID, notificationType, result, notification)
37-
} else if lh.LogLevel >= 3 { // Debug level
38+
} else if lh.LogLevel >= logging.LogLevelDebug { // Debug level
3839
internal.Logger.Printf(ctx, "hitless: conn[%d] %s notification processed successfully", connID, notificationType)
3940
}
4041
}
4142

4243
// NewLoggingHook creates a new logging hook with the specified log level.
43-
// Log levels: 0=errors, 1=warnings, 2=info, 3=debug
44-
func NewLoggingHook(logLevel int) *LoggingHook {
44+
// Log levels: LogLevelError=errors, LogLevelWarn=warnings, LogLevelInfo=info, LogLevelDebug=debug
45+
func NewLoggingHook(logLevel logging.LogLevel) *LoggingHook {
4546
return &LoggingHook{LogLevel: logLevel}
4647
}

hitless/notification_handler.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (snh *NotificationHandler) handleMoving(ctx context.Context, handlerCtx pus
106106
deadline := time.Now().Add(time.Duration(timeS) * time.Second)
107107
// If newEndpoint is empty, we should schedule a handoff to the current endpoint in timeS/2 seconds
108108
if newEndpoint == "" || newEndpoint == internal.RedisNull {
109-
if snh.manager.config.LogLevel >= 3 { // Debug level
109+
if snh.manager.config.LogLevel >= LogLevelDebug { // Debug level
110110
internal.Logger.Printf(ctx, "hitless: conn[%d] scheduling handoff to current endpoint in %v seconds",
111111
poolConn.GetID(), timeS/2)
112112
}
@@ -169,7 +169,7 @@ func (snh *NotificationHandler) handleMigrating(ctx context.Context, handlerCtx
169169
}
170170

171171
// Apply relaxed timeout to this specific connection
172-
if snh.manager.config.LogLevel >= 3 { // Debug level
172+
if snh.manager.config.LogLevel >= LogLevelDebug { // Debug level
173173
internal.Logger.Printf(ctx, "hitless: conn[%d] applying relaxed timeout (%v) for MIGRATING notification",
174174
conn.GetID(),
175175
snh.manager.config.RelaxedTimeout)
@@ -199,7 +199,7 @@ func (snh *NotificationHandler) handleMigrated(ctx context.Context, handlerCtx p
199199
}
200200

201201
// Clear relaxed timeout for this specific connection
202-
if snh.manager.config.LogLevel >= 3 { // Debug level
202+
if snh.manager.config.LogLevel >= LogLevelDebug { // Debug level
203203
connID := conn.GetID()
204204
internal.Logger.Printf(ctx, "hitless: conn[%d] clearing relaxed timeout for MIGRATED notification", connID)
205205
}
@@ -228,7 +228,7 @@ func (snh *NotificationHandler) handleFailingOver(ctx context.Context, handlerCt
228228
}
229229

230230
// Apply relaxed timeout to this specific connection
231-
if snh.manager.config.LogLevel >= 3 { // Debug level
231+
if snh.manager.config.LogLevel >= LogLevelDebug { // Debug level
232232
connID := conn.GetID()
233233
internal.Logger.Printf(ctx, "hitless: conn[%d] applying relaxed timeout (%v) for FAILING_OVER notification", connID, snh.manager.config.RelaxedTimeout)
234234
}
@@ -257,7 +257,7 @@ func (snh *NotificationHandler) handleFailedOver(ctx context.Context, handlerCtx
257257
}
258258

259259
// Clear relaxed timeout for this specific connection
260-
if snh.manager.config.LogLevel >= 3 { // Debug level
260+
if snh.manager.config.LogLevel >= LogLevelDebug { // Debug level
261261
connID := conn.GetID()
262262
internal.Logger.Printf(ctx, "hitless: conn[%d] clearing relaxed timeout for FAILED_OVER notification", connID)
263263
}

hitless/pool_hook.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (ph *PoolHook) onDemandWorker() {
217217

218218
case <-time.After(ph.workerTimeout):
219219
// Worker has been idle for too long, exit to save resources
220-
if ph.config != nil && ph.config.LogLevel >= 3 { // Debug level
220+
if ph.config != nil && ph.config.LogLevel >= LogLevelDebug { // Debug level
221221
internal.Logger.Printf(context.Background(),
222222
"hitless: worker exiting due to inactivity timeout (%v)", ph.workerTimeout)
223223
}
@@ -258,6 +258,7 @@ func (ph *PoolHook) processHandoffRequest(request HandoffRequest) {
258258

259259
// Perform the handoff with cancellable context
260260
shouldRetry, err := ph.performConnectionHandoffWithPool(shutdownCtx, request.Conn, request.Pool)
261+
minRetryBackoff := 500 * time.Millisecond
261262
if err != nil {
262263
if shouldRetry {
263264
now := time.Now()
@@ -268,20 +269,31 @@ func (ph *PoolHook) processHandoffRequest(request HandoffRequest) {
268269
}
269270

270271
afterTime := deadline.Sub(now)
271-
if afterTime < handoffTimeout/2 {
272+
if afterTime > handoffTimeout/2 {
272273
afterTime = handoffTimeout / 2
273274
}
275+
if afterTime < minRetryBackoff {
276+
afterTime = minRetryBackoff
277+
}
274278

275279
internal.Logger.Printf(context.Background(), "Handoff failed for conn[%d] WILL RETRY After %v: %v", request.ConnID, afterTime, err)
276280
time.AfterFunc(afterTime, func() {
277281
if err := ph.queueHandoff(request.Conn); err != nil {
278282
internal.Logger.Printf(context.Background(), "can't queue handoff for retry: %v", err)
279-
ph.removeConn(ctx, request, err)
283+
ph.removeConn(context.Background(), request, err)
280284
}
281285
})
286+
return
282287
} else {
283288
go ph.removeConn(ctx, request, err)
284289
}
290+
291+
// Clear handoff state if not returned for retry
292+
seqID := request.Conn.GetMovingSeqID()
293+
connID := request.Conn.GetID()
294+
if ph.hitlessManager != nil {
295+
ph.hitlessManager.UntrackOperationWithConnID(seqID, connID)
296+
}
285297
}
286298
}
287299

@@ -290,14 +302,14 @@ func (ph *PoolHook) removeConn(ctx context.Context, request HandoffRequest, err
290302
conn := request.Conn
291303
if pooler != nil {
292304
pooler.Remove(ctx, conn, err)
293-
if ph.config != nil && ph.config.LogLevel >= 1 { // Warning level
305+
if ph.config != nil && ph.config.LogLevel >= LogLevelWarn { // Warning level
294306
internal.Logger.Printf(ctx,
295307
"hitless: removed connection %d from pool due to max handoff retries reached",
296308
conn.GetID())
297309
}
298310
} else {
299311
conn.Close()
300-
if ph.config != nil && ph.config.LogLevel >= 1 { // Warning level
312+
if ph.config != nil && ph.config.LogLevel >= LogLevelWarn { // Warning level
301313
internal.Logger.Printf(ctx,
302314
"hitless: no pool provided for connection %d, cannot remove due to handoff initialization failure: %v",
303315
conn.GetID(), err)
@@ -335,7 +347,7 @@ func (ph *PoolHook) queueHandoff(conn *pool.Conn) error {
335347
// Queue is full - log and attempt scaling
336348
queueLen := len(ph.handoffQueue)
337349
queueCap := cap(ph.handoffQueue)
338-
if ph.config != nil && ph.config.LogLevel >= 1 { // Warning level
350+
if ph.config != nil && ph.config.LogLevel >= LogLevelWarn { // Warning level
339351
internal.Logger.Printf(context.Background(),
340352
"hitless: handoff queue is full (%d/%d), attempting timeout queuing and scaling workers",
341353
queueLen, queueCap)
@@ -352,14 +364,8 @@ func (ph *PoolHook) queueHandoff(conn *pool.Conn) error {
352364
// When error is returned, the connection handoff should be retried if err is not ErrMaxHandoffRetriesReached
353365
func (ph *PoolHook) performConnectionHandoffWithPool(ctx context.Context, conn *pool.Conn, pooler pool.Pooler) (shouldRetry bool, err error) {
354366
// Clear handoff state after successful handoff
355-
seqID := conn.GetMovingSeqID()
356367
connID := conn.GetID()
357368

358-
// Notify hitless manager of completion if available
359-
if ph.hitlessManager != nil {
360-
defer ph.hitlessManager.UntrackOperationWithConnID(seqID, connID)
361-
}
362-
363369
newEndpoint := conn.GetHandoffEndpoint()
364370
if newEndpoint == "" {
365371
return false, ErrConnectionInvalidHandoffState
@@ -373,7 +379,7 @@ func (ph *PoolHook) performConnectionHandoffWithPool(ctx context.Context, conn *
373379
}
374380

375381
if retries > maxRetries {
376-
if ph.config != nil && ph.config.LogLevel >= 1 { // Warning level
382+
if ph.config != nil && ph.config.LogLevel >= LogLevelWarn { // Warning level
377383
internal.Logger.Printf(ctx,
378384
"hitless: reached max retries (%d) for handoff of connection %d to %s",
379385
maxRetries, conn.GetID(), conn.GetHandoffEndpoint())
@@ -425,8 +431,8 @@ func (ph *PoolHook) performConnectionHandoffWithPool(ctx context.Context, conn *
425431

426432
if ph.config.LogLevel >= 2 { // Info level
427433
internal.Logger.Printf(context.Background(),
428-
"hitless: applied post-handoff relaxed timeout (%v) until %v for connection %d",
429-
relaxedTimeout, deadline.Format("15:04:05.000"), connID)
434+
"hitless: conn[%d] applied post-handoff relaxed timeout (%v) until %v",
435+
connID, relaxedTimeout, deadline.Format("15:04:05.000"))
430436
}
431437
}
432438

internal/log.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ import (
66
"log"
77
"os"
88
"strings"
9+
10+
"github.com/redis/go-redis/v9/logging"
11+
)
12+
13+
// LogLevel is an alias to the public logging.LogLevel for internal use
14+
type LogLevel = logging.LogLevel
15+
16+
// Log level constants for internal use
17+
const (
18+
LogLevelError = logging.LogLevelError
19+
LogLevelWarn = logging.LogLevelWarn
20+
LogLevelInfo = logging.LogLevelInfo
21+
LogLevelDebug = logging.LogLevelDebug
922
)
1023

1124
// TODO (ned): Revisit logging

0 commit comments

Comments
 (0)