Skip to content

Commit 1899868

Browse files
committed
update doc and config
1 parent 3417164 commit 1899868

File tree

5 files changed

+175
-99
lines changed

5 files changed

+175
-99
lines changed

hitless/README.md

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ The hitless upgrade system integrates with go-redis through pool hooks:
1919
- `FAILING_OVER` - Apply relaxed timeouts during failover
2020
- `FAILED_OVER` - Clear relaxed timeouts after failover
2121

22+
### Worker Management
23+
The hitless upgrade system uses **on-demand workers** for processing connection handoffs:
24+
25+
- **No minimum workers**: Workers are created only when needed
26+
- **Automatic scaling**: Workers scale up to `MaxWorkers` under load
27+
- **Automatic cleanup**: Idle workers are automatically terminated
28+
- **Efficient resource usage**: No resources consumed when idle
29+
- **Burst handling**: Can quickly scale to handle traffic spikes
30+
- **Smart defaults**: Auto-calculated as `min(10, PoolSize/3)` to prevent over-allocation
31+
- **Performance guarantee**: Explicit values enforced to minimum of 10 workers
32+
33+
#### MaxWorkers Configuration Logic
34+
- **When not set (0)**: `min(10, PoolSize/3)` - scales with pool size but caps at 10
35+
- **When explicitly set**: `max(10, set_value)` - ensures minimum 10 workers for performance
36+
37+
This design ensures optimal resource utilization while maintaining responsiveness during Redis topology changes.
38+
2239
## Configuration Examples
2340

2441
### Basic Setup
@@ -47,13 +64,14 @@ opt := &redis.Options{
4764
HitlessUpgrades: &redis.HitlessUpgradeConfig{
4865
Enabled: true,
4966
Config: &hitless.Config{
50-
MaxRetries: 3,
51-
RetryDelay: time.Second,
52-
HandoffTimeout: 30 * time.Second,
53-
RelaxedTimeout: 10 * time.Second,
54-
PostHandoffDuration: 5 * time.Second,
55-
LogLevel: 1,
56-
MaxWorkers: 10,
67+
MaxHandoffRetries: 3,
68+
HandoffRetryDelay: time.Second,
69+
HandoffTimeout: 30 * time.Second,
70+
RelaxedTimeout: 10 * time.Second,
71+
PostHandoffRelaxedDuration: 5 * time.Second,
72+
LogLevel: 1,
73+
MaxWorkers: 15, // On-demand workers (default: min(10, PoolSize/3), enforced min: 10)
74+
HandoffQueueSize: 50, // Queue size for handoff requests
5775
},
5876
},
5977
}
@@ -62,6 +80,52 @@ client := redis.NewClient(opt)
6280
defer client.Close()
6381
```
6482

83+
### Configuration Examples
84+
85+
#### MaxWorkers Auto-Calculation
86+
```go
87+
// Small pool - conservative worker allocation
88+
opt := &redis.Options{
89+
PoolSize: 6, // MaxWorkers will be min(10, 6/3) = 2
90+
HitlessUpgrades: &redis.HitlessUpgradeConfig{Enabled: true},
91+
}
92+
93+
// Medium pool - proportional worker allocation
94+
opt := &redis.Options{
95+
PoolSize: 30, // MaxWorkers will be min(10, 30/3) = 10
96+
HitlessUpgrades: &redis.HitlessUpgradeConfig{Enabled: true},
97+
}
98+
99+
// Large pool - capped worker allocation
100+
opt := &redis.Options{
101+
PoolSize: 120, // MaxWorkers will be min(10, 120/3) = 10 (capped)
102+
HitlessUpgrades: &redis.HitlessUpgradeConfig{Enabled: true},
103+
}
104+
```
105+
106+
#### Explicit MaxWorkers Configuration
107+
```go
108+
// Small explicit value - enforced minimum
109+
opt := &redis.Options{
110+
HitlessUpgrades: &redis.HitlessUpgradeConfig{
111+
Enabled: true,
112+
Config: &hitless.Config{
113+
MaxWorkers: 5, // Will be enforced to 10 for performance
114+
},
115+
},
116+
}
117+
118+
// Large explicit value - respected as-is
119+
opt := &redis.Options{
120+
HitlessUpgrades: &redis.HitlessUpgradeConfig{
121+
Enabled: true,
122+
Config: &hitless.Config{
123+
MaxWorkers: 20, // Will be kept as 20
124+
},
125+
},
126+
}
127+
```
128+
65129
### Cluster Client
66130
```go
67131
cluster := redis.NewClusterClient(&redis.ClusterOptions{

hitless/config.go

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,19 @@ type Config struct {
8686
// Default: 15 seconds (matches server-side eviction timeout)
8787
HandoffTimeout time.Duration
8888

89-
// MinWorkers is the minimum number of worker goroutines for processing handoff requests.
90-
// The processor starts with this number of workers and scales down to this level when idle.
91-
// If zero, defaults to max(1, PoolSize/50) to be proportional to connection pool size.
92-
//
93-
// Default: max(1, PoolSize/50)
94-
MinWorkers int
95-
9689
// MaxWorkers is the maximum number of worker goroutines for processing handoff requests.
97-
// The processor will scale up to this number when under load.
98-
// If zero, defaults to max(MinWorkers*4, PoolSize/10) to handle bursts effectively.
90+
// Workers are created on-demand and automatically cleaned up when idle.
91+
// If zero, defaults to min(10, PoolSize/3) to handle bursts effectively.
92+
// If explicitly set, enforces minimum of 10 workers.
9993
//
100-
// Default: max(MinWorkers*4, PoolSize/10)
94+
// Default: min(10, PoolSize/3), Minimum when set: 10
10195
MaxWorkers int
10296

10397
// HandoffQueueSize is the size of the buffered channel used to queue handoff requests.
10498
// If the queue is full, new handoff requests will be rejected.
99+
// Always capped by pool size since you can't handoff more connections than exist.
105100
//
106-
// Default: 10x max workers, but never more than pool size, min 2
101+
// Default: 10x max workers, capped by pool size, min 2
107102
HandoffQueueSize int
108103

109104
// PostHandoffRelaxedDuration is how long to keep relaxed timeouts on the new connection
@@ -198,7 +193,6 @@ func DefaultConfig() *Config {
198193
EndpointType: EndpointTypeAuto, // Auto-detect based on connection
199194
RelaxedTimeout: 30 * time.Second,
200195
HandoffTimeout: 15 * time.Second,
201-
MinWorkers: 0, // Auto-calculated based on pool size
202196
MaxWorkers: 0, // Auto-calculated based on pool size
203197
HandoffQueueSize: 0, // Auto-calculated based on max workers
204198
PostHandoffRelaxedDuration: 0, // Auto-calculated based on relaxed timeout
@@ -239,12 +233,9 @@ func (c *Config) Validate() error {
239233
}
240234
// Validate worker configuration
241235
// Allow 0 for auto-calculation, but negative values are invalid
242-
if c.MinWorkers < 0 {
236+
if c.MaxWorkers < 0 {
243237
return ErrInvalidHandoffWorkers
244238
}
245-
if c.MinWorkers > 0 && c.MaxWorkers > 0 && c.MaxWorkers < c.MinWorkers {
246-
return ErrInvalidWorkerRange
247-
}
248239
// HandoffQueueSize validation - allow 0 for auto-calculation
249240
if c.HandoffQueueSize < 0 {
250241
return ErrInvalidHandoffQueueSize
@@ -369,7 +360,6 @@ func (c *Config) ApplyDefaultsWithPoolSize(poolSize int) *Config {
369360
}
370361

371362
// Copy worker configuration
372-
result.MinWorkers = c.MinWorkers
373363
result.MaxWorkers = c.MaxWorkers
374364

375365
// Apply worker defaults based on pool size
@@ -383,6 +373,10 @@ func (c *Config) ApplyDefaultsWithPoolSize(poolSize int) *Config {
383373
} else {
384374
result.HandoffQueueSize = c.HandoffQueueSize
385375
}
376+
377+
// Always cap queue size by pool size - no point having more queue slots than connections
378+
result.HandoffQueueSize = util.Min(result.HandoffQueueSize, poolSize)
379+
386380
// Ensure minimum queue size of 2
387381
if result.HandoffQueueSize < 2 {
388382
result.HandoffQueueSize = 2
@@ -491,7 +485,6 @@ func (c *Config) Clone() *Config {
491485
EndpointType: c.EndpointType,
492486
RelaxedTimeout: c.RelaxedTimeout,
493487
HandoffTimeout: c.HandoffTimeout,
494-
MinWorkers: c.MinWorkers,
495488
MaxWorkers: c.MaxWorkers,
496489
HandoffQueueSize: c.HandoffQueueSize,
497490
PostHandoffRelaxedDuration: c.PostHandoffRelaxedDuration,
@@ -521,19 +514,17 @@ func (c *Config) applyWorkerDefaults(poolSize int) {
521514
poolSize = 10 * runtime.GOMAXPROCS(0)
522515
}
523516

524-
// MinWorkers: max(1, poolSize/50) - conservative baseline
525-
if c.MinWorkers == 0 {
526-
c.MinWorkers = util.Max(1, poolSize/50)
527-
}
528-
529-
// MaxWorkers: max(MinWorkers*4, poolSize/10) - handle bursts effectively
530517
if c.MaxWorkers == 0 {
531-
c.MaxWorkers = util.Max(c.MinWorkers*4, poolSize/10)
518+
// When not set: min(10, poolSize/3) - don't exceed 10 workers for small pools
519+
c.MaxWorkers = util.Min(10, poolSize/3)
520+
} else {
521+
// When explicitly set: max(10, set_value) - ensure at least 10 workers
522+
c.MaxWorkers = util.Max(10, c.MaxWorkers)
532523
}
533524

534-
// Ensure MaxWorkers >= MinWorkers
535-
if c.MaxWorkers < c.MinWorkers {
536-
c.MaxWorkers = c.MinWorkers
525+
// Ensure minimum of 1 worker (fallback for very small pools)
526+
if c.MaxWorkers < 1 {
527+
c.MaxWorkers = 1
537528
}
538529
}
539530

0 commit comments

Comments
 (0)