@@ -9,13 +9,15 @@ import (
9
9
"testing"
10
10
"time"
11
11
12
+ "github.com/stretchr/testify/assert"
12
13
"github.com/zalando/skipper/routing"
13
14
)
14
15
15
16
const (
16
- fadeInDuration = 500 * time .Millisecond
17
- bucketCount = 20
18
- monotonyTolerance = 0.4 // we need to use a high tolerance for CI testing
17
+ fadeInDuration = 500 * time .Millisecond
18
+ fadeInDurationHuge = 24 * time .Hour // we need this to be sure we're at the very beginning of fading in
19
+ bucketCount = 20
20
+ monotonyTolerance = 0.4 // we need to use a high tolerance for CI testing
19
21
)
20
22
21
23
func absint (i int ) int {
@@ -42,41 +44,46 @@ func checkMonotony(direction, prev, next int) bool {
42
44
}
43
45
}
44
46
47
+ func initializeEndpoints (endpointAges []time.Duration , fadeInDuration time.Duration ) (* routing.LBContext , []string ) {
48
+ var detectionTimes []time.Time
49
+ now := time .Now ()
50
+ for _ , ea := range endpointAges {
51
+ detectionTimes = append (detectionTimes , now .Add (- ea ))
52
+ }
53
+
54
+ var eps []string
55
+ for i := range endpointAges {
56
+ eps = append (eps , fmt .Sprintf ("ep-%d-%s.test" , i , endpointAges [i ]))
57
+ }
58
+
59
+ ctx := & routing.LBContext {
60
+ Params : map [string ]interface {}{},
61
+ Route : & routing.Route {
62
+ LBFadeInDuration : fadeInDuration ,
63
+ LBFadeInExponent : 1 ,
64
+ },
65
+ }
66
+
67
+ for i := range eps {
68
+ ctx .Route .LBEndpoints = append (ctx .Route .LBEndpoints , routing.LBEndpoint {
69
+ Host : eps [i ],
70
+ Detected : detectionTimes [i ],
71
+ })
72
+ }
73
+
74
+ return ctx , eps
75
+ }
76
+
45
77
func testFadeIn (
46
78
t * testing.T ,
47
79
name string ,
48
80
algorithm func ([]string ) routing.LBAlgorithm ,
49
81
endpointAges ... time.Duration ,
50
82
) {
51
83
t .Run (name , func (t * testing.T ) {
52
- var detectionTimes []time.Time
53
- now := time .Now ()
54
- for _ , ea := range endpointAges {
55
- detectionTimes = append (detectionTimes , now .Add (- ea ))
56
- }
57
-
58
- var eps []string
59
- for i := range endpointAges {
60
- eps = append (eps , string ('a' + rune (i )))
61
- }
84
+ ctx , eps := initializeEndpoints (endpointAges , fadeInDuration )
62
85
63
86
a := algorithm (eps )
64
-
65
- ctx := & routing.LBContext {
66
- Params : map [string ]interface {}{},
67
- Route : & routing.Route {
68
- LBFadeInDuration : fadeInDuration ,
69
- LBFadeInExponent : 1 ,
70
- },
71
- }
72
-
73
- for i := range eps {
74
- ctx .Route .LBEndpoints = append (ctx .Route .LBEndpoints , routing.LBEndpoint {
75
- Host : eps [i ],
76
- Detected : detectionTimes [i ],
77
- })
78
- }
79
-
80
87
rnd := rand .New (rand .NewSource (time .Now ().UnixNano ()))
81
88
t .Log ("test start" , time .Now ())
82
89
var stats []string
@@ -154,6 +161,11 @@ func testFadeIn(
154
161
})
155
162
}
156
163
164
+ func newConsistentHashForTest (endpoints []string ) routing.LBAlgorithm {
165
+ // The default parameter 100 is too small to get even distribution
166
+ return newConsistentHashInternal (endpoints , 1000 )
167
+ }
168
+
157
169
func TestFadeIn (t * testing.T ) {
158
170
old := 2 * fadeInDuration
159
171
testFadeIn (t , "round-robin, 0" , newRoundRobin , old , old )
@@ -178,16 +190,112 @@ func TestFadeIn(t *testing.T) {
178
190
testFadeIn (t , "random, 8" , newRandom , 0 , 0 , 0 , 0 , 0 , 0 )
179
191
testFadeIn (t , "random, 9" , newRandom , fadeInDuration / 2 , fadeInDuration / 3 , fadeInDuration / 4 )
180
192
181
- testFadeIn (t , "consistent-hash, 0" , newConsistentHash , old , old )
182
- testFadeIn (t , "consistent-hash, 1" , newConsistentHash , 0 , old )
183
- testFadeIn (t , "consistent-hash, 2" , newConsistentHash , 0 , 0 )
184
- testFadeIn (t , "consistent-hash, 3" , newConsistentHash , old , 0 )
185
- testFadeIn (t , "consistent-hash, 4" , newConsistentHash , old , old , old , 0 )
186
- testFadeIn (t , "consistent-hash, 5" , newConsistentHash , old , old , old , 0 , 0 , 0 )
187
- testFadeIn (t , "consistent-hash, 6" , newConsistentHash , old , 0 , 0 , 0 )
188
- testFadeIn (t , "consistent-hash, 7" , newConsistentHash , old , 0 , 0 , 0 , 0 , 0 , 0 )
189
- testFadeIn (t , "consistent-hash, 8" , newConsistentHash , 0 , 0 , 0 , 0 , 0 , 0 )
190
- testFadeIn (t , "consistent-hash, 9" , newConsistentHash , fadeInDuration / 2 , fadeInDuration / 3 , fadeInDuration / 4 )
193
+ testFadeIn (t , "consistent-hash, 0" , newConsistentHashForTest , old , old )
194
+ testFadeIn (t , "consistent-hash, 1" , newConsistentHashForTest , 0 , old )
195
+ testFadeIn (t , "consistent-hash, 2" , newConsistentHashForTest , 0 , 0 )
196
+ testFadeIn (t , "consistent-hash, 3" , newConsistentHashForTest , old , 0 )
197
+ testFadeIn (t , "consistent-hash, 4" , newConsistentHashForTest , old , old , old , 0 )
198
+ testFadeIn (t , "consistent-hash, 5" , newConsistentHashForTest , old , old , old , 0 , 0 , 0 )
199
+ testFadeIn (t , "consistent-hash, 6" , newConsistentHashForTest , old , 0 , 0 , 0 )
200
+ testFadeIn (t , "consistent-hash, 7" , newConsistentHashForTest , old , 0 , 0 , 0 , 0 , 0 , 0 )
201
+ testFadeIn (t , "consistent-hash, 8" , newConsistentHashForTest , 0 , 0 , 0 , 0 , 0 , 0 )
202
+ testFadeIn (t , "consistent-hash, 9" , newConsistentHashForTest , fadeInDuration / 2 , fadeInDuration / 3 , fadeInDuration / 4 )
203
+ }
204
+
205
+ func testFadeInLoadBetweenOldEps (
206
+ t * testing.T ,
207
+ name string ,
208
+ algorithm func ([]string ) routing.LBAlgorithm ,
209
+ nOld int , nNew int ,
210
+ ) {
211
+ t .Run (name , func (t * testing.T ) {
212
+ const (
213
+ numberOfReqs = 100000
214
+ acceptableErrorNearZero = 10
215
+ old = fadeInDurationHuge
216
+ new = time .Duration (0 )
217
+ )
218
+ endpointAges := []time.Duration {}
219
+ for i := 0 ; i < nOld ; i ++ {
220
+ endpointAges = append (endpointAges , old )
221
+ }
222
+ for i := 0 ; i < nNew ; i ++ {
223
+ endpointAges = append (endpointAges , new )
224
+ }
225
+
226
+ ctx , eps := initializeEndpoints (endpointAges , fadeInDurationHuge )
227
+
228
+ a := algorithm (eps )
229
+ rnd := rand .New (rand .NewSource (time .Now ().UnixNano ()))
230
+ nReqs := map [string ]int {}
231
+
232
+ t .Log ("test start" , time .Now ())
233
+ // Emulate the load balancer loop, sending requests to it with random hash keys
234
+ // over and over again till fadeIn period is over.
235
+ for i := 0 ; i < numberOfReqs ; i ++ {
236
+ ctx .Params [ConsistentHashKey ] = strconv .Itoa (rnd .Intn (100000 ))
237
+ ep := a .Apply (ctx )
238
+ nReqs [ep .Host ]++
239
+ }
240
+
241
+ expectedReqsPerOldEndpoint := numberOfReqs / nOld
242
+ for idx , ep := range eps {
243
+ if endpointAges [idx ] == old {
244
+ assert .InEpsilon (t , expectedReqsPerOldEndpoint , nReqs [ep ], 0.2 )
245
+ }
246
+ if endpointAges [idx ] == new {
247
+ assert .InDelta (t , 0 , nReqs [ep ], acceptableErrorNearZero )
248
+ }
249
+ }
250
+ })
251
+ }
252
+
253
+ func TestFadeInLoadBetweenOldEps (t * testing.T ) {
254
+ for nOld := 1 ; nOld < 6 ; nOld ++ {
255
+ for nNew := 0 ; nNew < 6 ; nNew ++ {
256
+ testFadeInLoadBetweenOldEps (t , fmt .Sprintf ("consistent-hash, %d old, %d new" , nOld , nNew ), newConsistentHash , nOld , nNew )
257
+ testFadeInLoadBetweenOldEps (t , fmt .Sprintf ("random, %d old, %d new" , nOld , nNew ), newRandom , nOld , nNew )
258
+ testFadeInLoadBetweenOldEps (t , fmt .Sprintf ("round-robin, %d old, %d new" , nOld , nNew ), newRoundRobin , nOld , nNew )
259
+ }
260
+ }
261
+ }
262
+
263
+ func testApplyEndsWhenAllEndpointsAreFading (
264
+ t * testing.T ,
265
+ name string ,
266
+ algorithm func ([]string ) routing.LBAlgorithm ,
267
+ nEndpoints int ,
268
+ ) {
269
+ t .Run (name , func (t * testing.T ) {
270
+ // Initialize every endpoint with zero: every endpoint is new
271
+ endpointAges := make ([]time.Duration , nEndpoints )
272
+
273
+ ctx , eps := initializeEndpoints (endpointAges , fadeInDurationHuge )
274
+
275
+ a := algorithm (eps )
276
+ ctx .Params [ConsistentHashKey ] = "someConstantString"
277
+ applied := make (chan struct {})
278
+
279
+ go func () {
280
+ a .Apply (ctx )
281
+ close (applied )
282
+ }()
283
+
284
+ select {
285
+ case <- time .After (time .Second ):
286
+ t .Errorf ("a.Apply has not finished in a reasonable time" )
287
+ case <- applied :
288
+ break
289
+ }
290
+ })
291
+ }
292
+
293
+ func TestApplyEndsWhenAllEndpointsAreFading (t * testing.T ) {
294
+ for nEndpoints := 1 ; nEndpoints < 10 ; nEndpoints ++ {
295
+ testApplyEndsWhenAllEndpointsAreFading (t , "consistent-hash" , newConsistentHash , nEndpoints )
296
+ testApplyEndsWhenAllEndpointsAreFading (t , "random" , newRandom , nEndpoints )
297
+ testApplyEndsWhenAllEndpointsAreFading (t , "round-robin" , newRoundRobin , nEndpoints )
298
+ }
191
299
}
192
300
193
301
func benchmarkFadeIn (
0 commit comments