...
1
18
19 package idle
20
21 import (
22 "context"
23 "fmt"
24 "sync"
25 "sync/atomic"
26 "testing"
27 "time"
28
29 "google.golang.org/grpc/internal/grpctest"
30 )
31
32 const (
33 defaultTestTimeout = 10 * time.Second
34 defaultTestIdleTimeout = 500 * time.Millisecond
35 defaultTestShortTimeout = 10 * time.Millisecond
36 )
37
38 type s struct {
39 grpctest.Tester
40 }
41
42 func Test(t *testing.T) {
43 grpctest.RunSubTests(t, s{})
44 }
45
46 type testEnforcer struct {
47 exitIdleCh chan struct{}
48 enterIdleCh chan struct{}
49 }
50
51 func (ti *testEnforcer) ExitIdleMode() error {
52 ti.exitIdleCh <- struct{}{}
53 return nil
54
55 }
56
57 func (ti *testEnforcer) EnterIdleMode() {
58 ti.enterIdleCh <- struct{}{}
59 }
60
61 func newTestEnforcer() *testEnforcer {
62 return &testEnforcer{
63 exitIdleCh: make(chan struct{}, 1),
64 enterIdleCh: make(chan struct{}, 1),
65 }
66 }
67
68
69
70 func overrideNewTimer(t *testing.T) <-chan struct{} {
71 t.Helper()
72
73 ch := make(chan struct{}, 1)
74 origTimeAfterFunc := timeAfterFunc
75 timeAfterFunc = func(d time.Duration, callback func()) *time.Timer {
76 return time.AfterFunc(d, func() {
77 select {
78 case ch <- struct{}{}:
79 default:
80 }
81 callback()
82 })
83 }
84 t.Cleanup(func() { timeAfterFunc = origTimeAfterFunc })
85 return ch
86 }
87
88
89
90
91
92
93
94 func (s) TestManager_Disabled(t *testing.T) {
95 callbackCh := overrideNewTimer(t)
96
97
98
99 enforcer := newTestEnforcer()
100 mgr := NewManager(enforcer, time.Duration(0))
101
102
103 select {
104 case <-callbackCh:
105 t.Fatal("Idle timer callback fired when manager is disabled")
106 case <-time.After(defaultTestShortTimeout):
107 }
108
109
110
111 go mgr.OnCallBegin()
112 select {
113 case <-enforcer.exitIdleCh:
114 case <-time.After(defaultTestShortTimeout):
115 t.Fatal("Timeout waiting for channel to move out of idle mode")
116 }
117
118
119
120
121
122 mgr.OnCallEnd()
123 mgr.OnCallEnd()
124
125
126
127
128 }
129
130
131
132
133 func (s) TestManager_Enabled_TimerFires(t *testing.T) {
134 callbackCh := overrideNewTimer(t)
135
136 enforcer := newTestEnforcer()
137 mgr := NewManager(enforcer, time.Duration(defaultTestIdleTimeout))
138 defer mgr.Close()
139 mgr.ExitIdleMode()
140
141
142 select {
143 case <-callbackCh:
144 case <-time.After(2 * defaultTestIdleTimeout):
145 t.Fatal("Timeout waiting for idle timer callback to fire")
146 }
147
148
149 select {
150 case <-enforcer.enterIdleCh:
151 case <-time.After(defaultTestTimeout):
152 t.Fatal("Timeout waiting for channel to move to idle")
153 }
154 }
155
156
157
158
159 func (s) TestManager_Enabled_OngoingCall(t *testing.T) {
160 callbackCh := overrideNewTimer(t)
161
162 enforcer := newTestEnforcer()
163 mgr := NewManager(enforcer, time.Duration(defaultTestIdleTimeout))
164 defer mgr.Close()
165 mgr.ExitIdleMode()
166
167
168
169 timerFired := make(chan struct{})
170 go func() {
171 mgr.OnCallBegin()
172 <-timerFired
173 mgr.OnCallEnd()
174 }()
175
176
177 select {
178 case <-callbackCh:
179 close(timerFired)
180 case <-time.After(2 * defaultTestIdleTimeout):
181 t.Fatal("Timeout waiting for idle timer callback to fire")
182 }
183
184
185
186 select {
187 case <-enforcer.enterIdleCh:
188 t.Fatalf("EnterIdleMode() called on enforcer when active RPC exists")
189 case <-time.After(defaultTestShortTimeout):
190 }
191
192
193
194 select {
195 case <-enforcer.enterIdleCh:
196 case <-time.After(defaultTestTimeout):
197 t.Fatal("Timeout waiting for channel to move to idle")
198 }
199 }
200
201
202
203
204
205 func (s) TestManager_Enabled_ActiveSinceLastCheck(t *testing.T) {
206 callbackCh := overrideNewTimer(t)
207
208 enforcer := newTestEnforcer()
209 mgr := NewManager(enforcer, time.Duration(defaultTestIdleTimeout))
210 defer mgr.Close()
211 mgr.ExitIdleMode()
212
213
214
215 timerFired := make(chan struct{})
216 go func() {
217 for ; ; <-time.After(defaultTestShortTimeout) {
218 mgr.OnCallBegin()
219 mgr.OnCallEnd()
220
221 select {
222 case <-timerFired:
223 return
224 default:
225 }
226 }
227 }()
228
229
230
231
232 select {
233 case <-callbackCh:
234 close(timerFired)
235 case <-time.After(2 * defaultTestIdleTimeout):
236 close(timerFired)
237 t.Fatal("Timeout waiting for idle timer callback to fire")
238 }
239 select {
240 case <-enforcer.enterIdleCh:
241 t.Fatalf("EnterIdleMode() called on enforcer when one RPC completed in the last period")
242 case <-time.After(defaultTestShortTimeout):
243 }
244
245
246
247 select {
248 case <-enforcer.enterIdleCh:
249 case <-time.After(defaultTestTimeout):
250 t.Fatal("Timeout waiting for channel to move to idle")
251 }
252 }
253
254
255
256
257 func (s) TestManager_Enabled_ExitIdleOnRPC(t *testing.T) {
258 overrideNewTimer(t)
259
260 enforcer := newTestEnforcer()
261 mgr := NewManager(enforcer, time.Duration(defaultTestIdleTimeout))
262 defer mgr.Close()
263
264 mgr.ExitIdleMode()
265 <-enforcer.exitIdleCh
266
267 select {
268 case <-enforcer.enterIdleCh:
269 case <-time.After(2 * defaultTestIdleTimeout):
270 t.Fatal("Timeout waiting for channel to move to idle mode")
271 }
272
273 for i := 0; i < 100; i++ {
274
275 go func() {
276 if err := mgr.OnCallBegin(); err != nil {
277 t.Errorf("OnCallBegin() failed: %v", err)
278 }
279 mgr.OnCallEnd()
280 }()
281 }
282
283
284 select {
285 case <-enforcer.exitIdleCh:
286 case <-time.After(2 * defaultTestIdleTimeout):
287 t.Fatal("Timeout waiting for channel to move out of idle mode")
288 }
289
290
291 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
292 defer sCancel()
293 select {
294 case <-enforcer.exitIdleCh:
295 t.Fatal("More than one call to exit idle mode on the ClientConn; only one expected")
296 case <-sCtx.Done():
297 }
298 }
299
300 type racyState int32
301
302 const (
303 stateInitial racyState = iota
304 stateEnteredIdle
305 stateExitedIdle
306 stateActiveRPCs
307 )
308
309
310
311 type racyEnforcer struct {
312 t *testing.T
313 state *racyState
314 started bool
315 }
316
317
318
319 func (ri *racyEnforcer) ExitIdleMode() error {
320
321 if ri.started == false {
322 if !atomic.CompareAndSwapInt32((*int32)(ri.state), int32(stateInitial), int32(stateInitial)) {
323 return fmt.Errorf("idleness enforcer's first ExitIdleMode after EnterIdleMode")
324 }
325 ri.started = true
326 return nil
327 }
328 if !atomic.CompareAndSwapInt32((*int32)(ri.state), int32(stateEnteredIdle), int32(stateExitedIdle)) {
329 return fmt.Errorf("idleness enforcer asked to exit idle when it did not enter idle earlier")
330 }
331 return nil
332 }
333
334
335 func (ri *racyEnforcer) EnterIdleMode() {
336 if !atomic.CompareAndSwapInt32((*int32)(ri.state), int32(stateInitial), int32(stateEnteredIdle)) {
337 ri.t.Errorf("idleness enforcer asked to enter idle after rpcs started")
338 }
339 }
340
341
342
343
344
345
346 func (s) TestManager_IdleTimeoutRacesWithOnCallBegin(t *testing.T) {
347
348 for i := 0; i < 20; i++ {
349 t.Run(fmt.Sprintf("iteration=%d", i), func(t *testing.T) {
350 var idlenessState racyState
351 enforcer := &racyEnforcer{t: t, state: &idlenessState}
352
353
354
355 mgr := NewManager(enforcer, time.Duration(10*time.Minute))
356 defer mgr.Close()
357 mgr.ExitIdleMode()
358
359 var wg sync.WaitGroup
360 wg.Add(1)
361 go func() {
362 defer wg.Done()
363 <-time.After(defaultTestIdleTimeout / 50)
364 mgr.handleIdleTimeout()
365 }()
366 for j := 0; j < 5; j++ {
367 wg.Add(1)
368 go func() {
369 defer wg.Done()
370
371
372 <-time.After(defaultTestIdleTimeout / 50)
373 if err := mgr.OnCallBegin(); err != nil {
374 t.Errorf("OnCallBegin() failed: %v", err)
375 }
376 atomic.StoreInt32((*int32)(&idlenessState), int32(stateActiveRPCs))
377 mgr.OnCallEnd()
378 }()
379 }
380 wg.Wait()
381 })
382 }
383 }
384
View as plain text