@@ -22,6 +22,7 @@ import (
22
22
"context"
23
23
"errors"
24
24
"fmt"
25
+ "io"
25
26
"strings"
26
27
"testing"
27
28
"time"
@@ -141,7 +142,8 @@ func (s) TestChannelIdleness_Disabled_NoActivity(t *testing.T) {
141
142
}
142
143
143
144
// Tests the case where channel idleness is enabled by passing a small value for
144
- // idle_timeout. Verifies that a READY channel with no RPCs moves to IDLE.
145
+ // idle_timeout. Verifies that a READY channel with no RPCs moves to IDLE, and
146
+ // the connection to the backend is closed.
145
147
func (s ) TestChannelIdleness_Enabled_NoActivity (t * testing.T ) {
146
148
// Create a ClientConn with a short idle_timeout.
147
149
r := manual .NewBuilderWithScheme ("whatever" )
@@ -158,7 +160,8 @@ func (s) TestChannelIdleness_Enabled_NoActivity(t *testing.T) {
158
160
t .Cleanup (func () { cc .Close () })
159
161
160
162
// Start a test backend and push an address update via the resolver.
161
- backend := stubserver .StartTestService (t , nil )
163
+ lis := testutils .NewListenerWrapper (t , nil )
164
+ backend := stubserver .StartTestService (t , & stubserver.StubServer {Listener : lis })
162
165
t .Cleanup (backend .Stop )
163
166
r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
164
167
@@ -167,92 +170,137 @@ func (s) TestChannelIdleness_Enabled_NoActivity(t *testing.T) {
167
170
defer cancel ()
168
171
testutils .AwaitState (ctx , t , cc , connectivity .Ready )
169
172
173
+ // Retrieve the wrapped conn from the listener.
174
+ v , err := lis .NewConnCh .Receive (ctx )
175
+ if err != nil {
176
+ t .Fatalf ("Failed to retrieve conn from test listener: %v" , err )
177
+ }
178
+ conn := v .(* testutils.ConnWrapper )
179
+
170
180
// Verify that the ClientConn moves to IDLE as there is no activity.
171
181
testutils .AwaitState (ctx , t , cc , connectivity .Idle )
172
182
173
183
// Verify idleness related channelz events.
174
184
if err := channelzTraceEventFound (ctx , "entering idle mode" ); err != nil {
175
185
t .Fatal (err )
176
186
}
187
+
188
+ // Verify that the previously open connection is closed.
189
+ if _ , err := conn .CloseCh .Receive (ctx ); err != nil {
190
+ t .Fatalf ("Failed when waiting for connection to be closed after channel entered IDLE: %v" , err )
191
+ }
177
192
}
178
193
179
194
// Tests the case where channel idleness is enabled by passing a small value for
180
195
// idle_timeout. Verifies that a READY channel with an ongoing RPC stays READY.
181
196
func (s ) TestChannelIdleness_Enabled_OngoingCall (t * testing.T ) {
182
- // Create a ClientConn with a short idle_timeout.
183
- r := manual .NewBuilderWithScheme ("whatever" )
184
- dopts := []grpc.DialOption {
185
- grpc .WithTransportCredentials (insecure .NewCredentials ()),
186
- grpc .WithResolvers (r ),
187
- grpc .WithIdleTimeout (defaultTestShortIdleTimeout ),
188
- grpc .WithDefaultServiceConfig (`{"loadBalancingConfig": [{"round_robin":{}}]}` ),
189
- }
190
- cc , err := grpc .Dial (r .Scheme ()+ ":///test.server" , dopts ... )
191
- if err != nil {
192
- t .Fatalf ("grpc.Dial() failed: %v" , err )
193
- }
194
- t .Cleanup (func () { cc .Close () })
195
-
196
- // Start a test backend which keeps a unary RPC call active by blocking on a
197
- // channel that is closed by the test later on. Also push an address update
198
- // via the resolver.
199
- blockCh := make (chan struct {})
200
- backend := & stubserver.StubServer {
201
- EmptyCallF : func (ctx context.Context , in * testpb.Empty ) (* testpb.Empty , error ) {
202
- <- blockCh
203
- return & testpb.Empty {}, nil
197
+ tests := []struct {
198
+ name string
199
+ makeRPC func (ctx context.Context , client testgrpc.TestServiceClient ) error
200
+ }{
201
+ {
202
+ name : "unary" ,
203
+ makeRPC : func (ctx context.Context , client testgrpc.TestServiceClient ) error {
204
+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); err != nil {
205
+ return fmt .Errorf ("EmptyCall RPC failed: %v" , err )
206
+ }
207
+ return nil
208
+ },
209
+ },
210
+ {
211
+ name : "streaming" ,
212
+ makeRPC : func (ctx context.Context , client testgrpc.TestServiceClient ) error {
213
+ stream , err := client .FullDuplexCall (ctx )
214
+ if err != nil {
215
+ t .Fatalf ("FullDuplexCall RPC failed: %v" , err )
216
+ }
217
+ if _ , err := stream .Recv (); err != nil && err != io .EOF {
218
+ t .Fatalf ("stream.Recv() failed: %v" , err )
219
+ }
220
+ return nil
221
+ },
204
222
},
205
223
}
206
- if err := backend .StartServer (); err != nil {
207
- t .Fatalf ("Failed to start backend: %v" , err )
208
- }
209
- t .Cleanup (backend .Stop )
210
- r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
211
-
212
- // Verify that the ClientConn moves to READY.
213
- ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
214
- defer cancel ()
215
- testutils .AwaitState (ctx , t , cc , connectivity .Ready )
216
224
217
- // Spawn a goroutine which checks expected state transitions and idleness
218
- // channelz trace events. It eventually closes `blockCh`, thereby unblocking
219
- // the server RPC handler and the unary call below.
220
- errCh := make (chan error , 1 )
221
- go func () {
222
- defer close (blockCh )
223
- // Verify that the ClientConn stays in READY.
224
- sCtx , sCancel := context .WithTimeout (ctx , 3 * defaultTestShortIdleTimeout )
225
- defer sCancel ()
226
- testutils .AwaitNoStateChange (sCtx , t , cc , connectivity .Ready )
227
-
228
- // Verify that there are no idleness related channelz events.
229
- if err := channelzTraceEventNotFound (ctx , "entering idle mode" ); err != nil {
230
- errCh <- err
231
- return
232
- }
233
- if err := channelzTraceEventNotFound (ctx , "exiting idle mode" ); err != nil {
234
- errCh <- err
235
- return
236
- }
225
+ for _ , test := range tests {
226
+ t .Run (test .name , func (t * testing.T ) {
227
+ // Create a ClientConn with a short idle_timeout.
228
+ r := manual .NewBuilderWithScheme ("whatever" )
229
+ dopts := []grpc.DialOption {
230
+ grpc .WithTransportCredentials (insecure .NewCredentials ()),
231
+ grpc .WithResolvers (r ),
232
+ grpc .WithIdleTimeout (defaultTestShortIdleTimeout ),
233
+ grpc .WithDefaultServiceConfig (`{"loadBalancingConfig": [{"round_robin":{}}]}` ),
234
+ }
235
+ cc , err := grpc .Dial (r .Scheme ()+ ":///test.server" , dopts ... )
236
+ if err != nil {
237
+ t .Fatalf ("grpc.Dial() failed: %v" , err )
238
+ }
239
+ t .Cleanup (func () { cc .Close () })
240
+
241
+ // Start a test backend which keeps a unary RPC call active by blocking on a
242
+ // channel that is closed by the test later on. Also push an address update
243
+ // via the resolver.
244
+ blockCh := make (chan struct {})
245
+ backend := & stubserver.StubServer {
246
+ EmptyCallF : func (ctx context.Context , in * testpb.Empty ) (* testpb.Empty , error ) {
247
+ <- blockCh
248
+ return & testpb.Empty {}, nil
249
+ },
250
+ FullDuplexCallF : func (stream testgrpc.TestService_FullDuplexCallServer ) error {
251
+ <- blockCh
252
+ return nil
253
+ },
254
+ }
255
+ if err := backend .StartServer (); err != nil {
256
+ t .Fatalf ("Failed to start backend: %v" , err )
257
+ }
258
+ t .Cleanup (backend .Stop )
259
+ r .UpdateState (resolver.State {Addresses : []resolver.Address {{Addr : backend .Address }}})
260
+
261
+ // Verify that the ClientConn moves to READY.
262
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
263
+ defer cancel ()
264
+ testutils .AwaitState (ctx , t , cc , connectivity .Ready )
265
+
266
+ // Spawn a goroutine which checks expected state transitions and idleness
267
+ // channelz trace events.
268
+ errCh := make (chan error , 1 )
269
+ go func () {
270
+ defer close (blockCh )
271
+
272
+ // Verify that the ClientConn stays in READY.
273
+ sCtx , sCancel := context .WithTimeout (ctx , 3 * defaultTestShortIdleTimeout )
274
+ defer sCancel ()
275
+ if cc .WaitForStateChange (sCtx , connectivity .Ready ) {
276
+ errCh <- fmt .Errorf ("state changed from %q to %q when no state change was expected" , connectivity .Ready , cc .GetState ())
277
+ return
278
+ }
237
279
238
- // Unblock the unary RPC on the server.
239
- errCh <- nil
240
- }()
280
+ // Verify that there are no idleness related channelz events.
281
+ //
282
+ // TODO: Improve the checks here. If these log strings are
283
+ // changed in the code, these checks will continue to pass.
284
+ if err := channelzTraceEventNotFound (ctx , "entering idle mode" ); err != nil {
285
+ errCh <- err
286
+ return
287
+ }
288
+ errCh <- channelzTraceEventNotFound (ctx , "exiting idle mode" )
289
+ }()
241
290
242
- // Make a unary RPC that blocks on the server, thereby ensuring that the
243
- // count of active RPCs on the client is non-zero.
244
- client := testgrpc .NewTestServiceClient (cc )
245
- if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); err != nil {
246
- t .Errorf ("EmptyCall RPC failed: %v" , err )
247
- }
291
+ if err := test .makeRPC (ctx , testgrpc .NewTestServiceClient (cc )); err != nil {
292
+ t .Fatalf ("%s rpc failed: %v" , test .name , err )
293
+ }
248
294
249
- select {
250
- case err := <- errCh :
251
- if err != nil {
252
- t .Fatal (err )
253
- }
254
- case <- ctx .Done ():
255
- t .Fatalf ("Timeout when trying to verify that an active RPC keeps channel from moving to IDLE" )
295
+ select {
296
+ case err := <- errCh :
297
+ if err != nil {
298
+ t .Fatal (err )
299
+ }
300
+ case <- ctx .Done ():
301
+ t .Fatalf ("Timeout when trying to verify that an active RPC keeps channel from moving to IDLE" )
302
+ }
303
+ })
256
304
}
257
305
}
258
306
0 commit comments