k8s.io/client-go@v0.31.1/util/flowcontrol/throttle_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package flowcontrol 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 "testing" 24 "time" 25 ) 26 27 func TestMultithreadedThrottling(t *testing.T) { 28 // Bucket with 100QPS and no burst 29 r := NewTokenBucketRateLimiter(100, 1) 30 31 // channel to collect 100 tokens 32 taken := make(chan bool, 100) 33 34 // Set up goroutines to hammer the throttler 35 startCh := make(chan bool) 36 endCh := make(chan bool) 37 for i := 0; i < 10; i++ { 38 go func() { 39 // wait for the starting signal 40 <-startCh 41 for { 42 // get a token 43 r.Accept() 44 select { 45 // try to add it to the taken channel 46 case taken <- true: 47 continue 48 // if taken is full, notify and return 49 default: 50 endCh <- true 51 return 52 } 53 } 54 }() 55 } 56 57 // record wall time 58 startTime := time.Now() 59 // take the initial capacity so all tokens are the result of refill 60 r.Accept() 61 // start the thundering herd 62 close(startCh) 63 // wait for the first signal that we collected 100 tokens 64 <-endCh 65 // record wall time 66 endTime := time.Now() 67 68 // tolerate a 1% clock change because these things happen 69 if duration := endTime.Sub(startTime); duration < (time.Second * 99 / 100) { 70 // We shouldn't be able to get 100 tokens out of the bucket in less than 1 second of wall clock time, no matter what 71 t.Errorf("Expected it to take at least 1 second to get 100 tokens, took %v", duration) 72 } else { 73 t.Logf("Took %v to get 100 tokens", duration) 74 } 75 } 76 77 func TestBasicThrottle(t *testing.T) { 78 r := NewTokenBucketRateLimiter(1, 3) 79 for i := 0; i < 3; i++ { 80 if !r.TryAccept() { 81 t.Error("unexpected false accept") 82 } 83 } 84 if r.TryAccept() { 85 t.Error("unexpected true accept") 86 } 87 } 88 89 func TestIncrementThrottle(t *testing.T) { 90 r := NewTokenBucketRateLimiter(1, 1) 91 if !r.TryAccept() { 92 t.Error("unexpected false accept") 93 } 94 if r.TryAccept() { 95 t.Error("unexpected true accept") 96 } 97 98 // Allow to refill 99 time.Sleep(2 * time.Second) 100 101 if !r.TryAccept() { 102 t.Error("unexpected false accept") 103 } 104 } 105 106 func TestThrottle(t *testing.T) { 107 r := NewTokenBucketRateLimiter(10, 5) 108 109 // Should consume 5 tokens immediately, then 110 // the remaining 11 should take at least 1 second (0.1s each) 111 expectedFinish := time.Now().Add(time.Second * 1) 112 for i := 0; i < 16; i++ { 113 r.Accept() 114 } 115 if time.Now().Before(expectedFinish) { 116 t.Error("rate limit was not respected, finished too early") 117 } 118 } 119 120 func TestAlwaysFake(t *testing.T) { 121 rl := NewFakeAlwaysRateLimiter() 122 if !rl.TryAccept() { 123 t.Error("TryAccept in AlwaysFake should return true.") 124 } 125 // If this will block the test will timeout 126 rl.Accept() 127 } 128 129 func TestNeverFake(t *testing.T) { 130 rl := NewFakeNeverRateLimiter() 131 if rl.TryAccept() { 132 t.Error("TryAccept in NeverFake should return false.") 133 } 134 135 finished := false 136 wg := sync.WaitGroup{} 137 wg.Add(1) 138 go func() { 139 rl.Accept() 140 finished = true 141 wg.Done() 142 }() 143 144 // Wait some time to make sure it never finished. 145 time.Sleep(time.Second) 146 if finished { 147 t.Error("Accept should block forever in NeverFake.") 148 } 149 150 rl.Stop() 151 wg.Wait() 152 if !finished { 153 t.Error("Stop should make Accept unblock in NeverFake.") 154 } 155 } 156 157 func TestWait(t *testing.T) { 158 r := NewTokenBucketRateLimiter(0.0001, 1) 159 160 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) 161 defer cancelFn() 162 if err := r.Wait(ctx); err != nil { 163 t.Errorf("unexpected wait failed, err: %v", err) 164 } 165 166 ctx2, cancelFn2 := context.WithTimeout(context.Background(), time.Second) 167 defer cancelFn2() 168 if err := r.Wait(ctx2); err == nil { 169 t.Errorf("unexpected wait success") 170 } else { 171 t.Log(fmt.Sprintf("wait err: %v", err)) 172 } 173 } 174 175 type fakeClock struct { 176 now time.Time 177 } 178 179 func newFakeClock() *fakeClock { 180 return &fakeClock{ 181 now: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), 182 } 183 } 184 185 func (fc *fakeClock) Now() time.Time { 186 return fc.now 187 } 188 189 func (fc *fakeClock) Sleep(d time.Duration) { 190 fc.now = fc.now.Add(d) 191 } 192 193 func (fc *fakeClock) Since(ts time.Time) time.Duration { 194 return time.Since(ts) 195 } 196 197 func TestRatePrecisionBug(t *testing.T) { 198 // golang.org/x/time/rate used to have bugs around precision and this 199 // proves that they don't recur (at least in the form we know about). This 200 // case is specifically designed to trigger the problem after 14 seconds. 201 qps := float32(time.Second) / float32(1031425*time.Microsecond) 202 clock := newFakeClock() 203 tb := NewTokenBucketRateLimiterWithClock(qps, 1, clock) 204 205 for i := 0; i < 60; i++ { 206 if !tb.TryAccept() { 207 t.Fatalf("failed after %d seconds", i*2) 208 } 209 clock.Sleep(2 * time.Second) 210 } 211 }