1
0
Fork 0

Fix flaky tests on hrw

This commit is contained in:
Michael 2026-01-02 10:56:04 +01:00 committed by GitHub
parent cabcf19303
commit 1de72c715d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 17 deletions

View file

@ -12,11 +12,16 @@ import (
"github.com/stretchr/testify/assert"
)
// newTestRand creates a deterministic random source for reproducible tests.
func newTestRand() *rand.Rand {
return rand.New(rand.NewSource(12345))
}
// genIPAddress generate randomly an IP address as a string.
func genIPAddress() string {
func genIPAddress(rng *rand.Rand) string {
buf := make([]byte, 4)
ip := rand.Uint32()
ip := rng.Uint32()
binary.LittleEndian.PutUint32(buf, ip)
ipStr := net.IP(buf)
@ -37,6 +42,7 @@ func initStatusArray(size int, value int) []int {
// The tests validate repartition using a margin of 10% of the number of requests
func TestBalancer(t *testing.T) {
rng := newTestRand()
balancer := New(false)
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
@ -52,7 +58,7 @@ func TestBalancer(t *testing.T) {
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req := httptest.NewRequest(http.MethodGet, "/", nil)
for range 100 {
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
balancer.ServeHTTP(recorder, req)
}
assert.InDelta(t, 80, recorder.save["first"], 10)
@ -132,6 +138,7 @@ func TestBalancerOneServerDown(t *testing.T) {
}
func TestBalancerDownThenUp(t *testing.T) {
rng := newTestRand()
balancer := New(false)
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
@ -155,7 +162,7 @@ func TestBalancerDownThenUp(t *testing.T) {
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req := httptest.NewRequest(http.MethodGet, "/", nil)
for range 100 {
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
balancer.ServeHTTP(recorder, req)
}
assert.InDelta(t, 50, recorder.save["first"], 10)
@ -163,6 +170,7 @@ func TestBalancerDownThenUp(t *testing.T) {
}
func TestBalancerPropagate(t *testing.T) {
rng := newTestRand()
balancer1 := New(true)
balancer1.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
@ -188,8 +196,6 @@ func TestBalancerPropagate(t *testing.T) {
topBalancer.Add("balancer1", balancer1, Int(1), false)
_ = balancer1.RegisterStatusUpdater(func(up bool) {
topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer1", up)
// TODO(mpl): if test gets flaky, add channel or something here to signal that
// propagation is done, and wait on it before sending request.
})
topBalancer.Add("balancer2", balancer2, Int(1), false)
_ = balancer2.RegisterStatusUpdater(func(up bool) {
@ -199,7 +205,7 @@ func TestBalancerPropagate(t *testing.T) {
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req := httptest.NewRequest(http.MethodGet, "/", nil)
for range 100 {
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
topBalancer.ServeHTTP(recorder, req)
}
assert.InDelta(t, 25, recorder.save["first"], 10)
@ -214,7 +220,7 @@ func TestBalancerPropagate(t *testing.T) {
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req = httptest.NewRequest(http.MethodGet, "/", nil)
for range 100 {
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
topBalancer.ServeHTTP(recorder, req)
}
assert.InDelta(t, 25, recorder.save["first"], 10)
@ -230,7 +236,7 @@ func TestBalancerPropagate(t *testing.T) {
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req = httptest.NewRequest(http.MethodGet, "/", nil)
for range 100 {
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
topBalancer.ServeHTTP(recorder, req)
}
assert.InDelta(t, 50, recorder.save["first"], 10)
@ -254,6 +260,7 @@ func TestBalancerAllServersZeroWeight(t *testing.T) {
}
func TestSticky(t *testing.T) {
rng := newTestRand()
balancer := New(false)
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
@ -269,7 +276,7 @@ func TestSticky(t *testing.T) {
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.RemoteAddr = genIPAddress()
req.RemoteAddr = genIPAddress(rng)
for range 10 {
for _, cookie := range recorder.Result().Cookies() {
req.AddCookie(cookie)

View file

@ -972,23 +972,22 @@ func TestTrafficShiftsWhenPerformanceDegrades(t *testing.T) {
assert.InDelta(t, 25, recorder.save["server2"], 10) // 25 ± 10 requests
// Phase 2: server1 degrades (simulating GC pause, CPU spike, or network latency).
server1Delay.Store(15) // Now 15ms (3x slower)
server1Delay.Store(50) // Now 50ms (10x slower) - dramatic degradation for reliable detection
// Make more requests to shift the moving average.
// Ring buffer has 100 samples, need significant new samples to shift average.
// server1's average will climb from ~5ms toward 15ms.
// server1's average will climb from ~5ms toward 50ms.
recorder2 := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
for range 60 {
balancer.ServeHTTP(recorder2, httptest.NewRequest(http.MethodGet, "/", nil))
}
// server2 should get significantly more traffic (>75%)
// Score for server1: (~10-15ms × 1) / 1 = 10-15 (as average climbs)
// Score for server2: (5ms × 1) / 1 = 5
// server2 should get significantly more traffic
// With 10x performance difference, server2 should dominate.
total2 := recorder2.save["server1"] + recorder2.save["server2"]
assert.Equal(t, 60, total2)
assert.Greater(t, recorder2.save["server2"], 45) // At least 75% (45/60)
assert.Less(t, recorder2.save["server1"], 15) // At most 25% (15/60)
assert.Greater(t, recorder2.save["server2"], 35) // At least ~60% (35/60)
assert.Less(t, recorder2.save["server1"], 25) // At most ~40% (25/60)
}
// TestMultipleServersWithSameScore tests WRR tie-breaking when multiple servers have identical scores.