1
0
mirror of https://github.com/redis/go-redis.git synced 2025-12-03 18:31:14 +03:00
Files
go-redis/adaptive_delay_test.go
2025-11-03 17:17:30 +02:00

294 lines
6.7 KiB
Go

package redis
import (
"testing"
"time"
)
// TestAdaptiveDelayCalculation tests the adaptive delay calculation logic
func TestAdaptiveDelayCalculation(t *testing.T) {
tests := []struct {
name string
maxBatchSize int
maxDelay time.Duration
adaptive bool
queueLen int
expected time.Duration
}{
// Disabled adaptive delay
{
name: "adaptive disabled, returns fixed delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: false,
queueLen: 50,
expected: 100 * time.Microsecond,
},
{
name: "adaptive disabled, zero delay",
maxBatchSize: 100,
maxDelay: 0,
adaptive: false,
queueLen: 50,
expected: 0,
},
// Enabled adaptive delay - 75% threshold
{
name: "75% full - no delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 75,
expected: 0,
},
{
name: "76% full - no delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 76,
expected: 0,
},
{
name: "100% full - no delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 100,
expected: 0,
},
// Enabled adaptive delay - 50% threshold
{
name: "50% full - 25% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 50,
expected: 25 * time.Microsecond,
},
{
name: "60% full - 25% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 60,
expected: 25 * time.Microsecond,
},
{
name: "74% full - 25% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 74,
expected: 25 * time.Microsecond,
},
// Enabled adaptive delay - 25% threshold
{
name: "25% full - 50% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 25,
expected: 50 * time.Microsecond,
},
{
name: "30% full - 50% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 30,
expected: 50 * time.Microsecond,
},
{
name: "49% full - 50% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 49,
expected: 50 * time.Microsecond,
},
// Enabled adaptive delay - <25% threshold
{
name: "24% full - 100% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 24,
expected: 100 * time.Microsecond,
},
{
name: "10% full - 100% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 10,
expected: 100 * time.Microsecond,
},
{
name: "1% full - 100% delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 1,
expected: 100 * time.Microsecond,
},
// Edge cases
{
name: "empty queue - no delay",
maxBatchSize: 100,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 0,
expected: 0,
},
{
name: "zero max delay - no delay",
maxBatchSize: 100,
maxDelay: 0,
adaptive: true,
queueLen: 50,
expected: 0,
},
// Different batch sizes
{
name: "small batch - 75% full",
maxBatchSize: 10,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 8,
expected: 0,
},
{
name: "small batch - 50% full",
maxBatchSize: 10,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 5,
expected: 25 * time.Microsecond,
},
{
name: "large batch - 75% full",
maxBatchSize: 1000,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 750,
expected: 0,
},
{
name: "large batch - 50% full",
maxBatchSize: 1000,
maxDelay: 100 * time.Microsecond,
adaptive: true,
queueLen: 500,
expected: 25 * time.Microsecond,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create autopipeliner with test config
ap := &AutoPipeliner{
config: &AutoPipelineConfig{
MaxBatchSize: tt.maxBatchSize,
MaxFlushDelay: tt.maxDelay,
AdaptiveDelay: tt.adaptive,
},
}
ap.queueLen.Store(int32(tt.queueLen))
// Calculate delay
result := ap.calculateDelay()
// Verify result
if result != tt.expected {
t.Errorf("calculateDelay() = %v, want %v", result, tt.expected)
}
})
}
}
// TestAdaptiveDelayIntegerArithmetic verifies integer arithmetic correctness
func TestAdaptiveDelayIntegerArithmetic(t *testing.T) {
maxBatch := 100
maxDelay := 100 * time.Microsecond
ap := &AutoPipeliner{
config: &AutoPipelineConfig{
MaxBatchSize: maxBatch,
MaxFlushDelay: maxDelay,
AdaptiveDelay: true,
},
}
// Test all queue lengths from 0 to maxBatch
for queueLen := 0; queueLen <= maxBatch; queueLen++ {
ap.queueLen.Store(int32(queueLen))
delay := ap.calculateDelay()
// Verify delay is one of the expected values
switch {
case queueLen == 0:
if delay != 0 {
t.Errorf("queueLen=%d: expected 0, got %v", queueLen, delay)
}
case queueLen*4 >= maxBatch*3: // ≥75%
if delay != 0 {
t.Errorf("queueLen=%d (≥75%%): expected 0, got %v", queueLen, delay)
}
case queueLen*2 >= maxBatch: // ≥50%
if delay != maxDelay/4 {
t.Errorf("queueLen=%d (≥50%%): expected %v, got %v", queueLen, maxDelay/4, delay)
}
case queueLen*4 >= maxBatch: // ≥25%
if delay != maxDelay/2 {
t.Errorf("queueLen=%d (≥25%%): expected %v, got %v", queueLen, maxDelay/2, delay)
}
default: // <25%
if delay != maxDelay {
t.Errorf("queueLen=%d (<25%%): expected %v, got %v", queueLen, maxDelay, delay)
}
}
}
}
// BenchmarkCalculateDelay benchmarks the delay calculation
func BenchmarkCalculateDelay(b *testing.B) {
ap := &AutoPipeliner{
config: &AutoPipelineConfig{
MaxBatchSize: 100,
MaxFlushDelay: 100 * time.Microsecond,
AdaptiveDelay: true,
},
}
ap.queueLen.Store(50)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = ap.calculateDelay()
}
}
// BenchmarkCalculateDelayDisabled benchmarks with adaptive delay disabled
func BenchmarkCalculateDelayDisabled(b *testing.B) {
ap := &AutoPipeliner{
config: &AutoPipelineConfig{
MaxBatchSize: 100,
MaxFlushDelay: 100 * time.Microsecond,
AdaptiveDelay: false,
},
}
ap.queueLen.Store(50)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = ap.calculateDelay()
}
}