Fix flaky tests on hrw
This commit is contained in:
parent
cabcf19303
commit
1de72c715d
2 changed files with 23 additions and 17 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue