@@ -469,4 +469,98 @@ void testRecycledChannelDoesNotRejoinPool() throws InterruptedException {
469469
470470 pool .close ();
471471 }
472+
473+ @ Test
474+ void testRecycleChannelOnConsecutiveFailures () {
475+ when (channelSupplier .get ()).thenReturn (channel );
476+ when (channel .newCall (any (), any ())).thenReturn (clientCall );
477+ doNothing ().when (clientCall ).start (listener .capture (), any ());
478+
479+ ChannelPoolDpImpl pool =
480+ new ChannelPoolDpImpl (channelSupplier , defaultConfig , debugTagTracer , bgExecutor );
481+
482+ for (int i = 0 ; i < 4 ; i ++) {
483+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
484+ .start (mock (Listener .class ), new Metadata ());
485+ listener .getValue ().onClose (Status .UNAVAILABLE , new Metadata ());
486+
487+ // Should not be recycled yet
488+ verify (channel , times (0 )).shutdown ();
489+ verify (channelSupplier , times (1 )).get ();
490+ }
491+
492+ // 5th failure
493+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
494+ .start (mock (Listener .class ), new Metadata ());
495+ listener .getValue ().onClose (Status .UNAVAILABLE , new Metadata ());
496+
497+ // Now it should be recycled
498+ verify (channel , times (1 )).shutdown ();
499+ verify (channelSupplier , times (2 )).get ();
500+
501+ pool .close ();
502+ }
503+
504+ @ Test
505+ void testResetConsecutiveFailuresOnSuccess () {
506+ when (channelSupplier .get ()).thenReturn (channel );
507+ when (channel .newCall (any (), any ())).thenReturn (clientCall );
508+ doNothing ().when (clientCall ).start (listener .capture (), any ());
509+ doReturn (Attributes .EMPTY ).when (clientCall ).getAttributes ();
510+
511+ ChannelPoolDpImpl pool =
512+ new ChannelPoolDpImpl (channelSupplier , defaultConfig , debugTagTracer , bgExecutor );
513+
514+ // 4 failures
515+ for (int i = 0 ; i < 4 ; i ++) {
516+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
517+ .start (mock (Listener .class ), new Metadata ());
518+ listener .getValue ().onClose (Status .UNAVAILABLE , new Metadata ());
519+ }
520+ verify (channel , times (0 )).shutdown ();
521+
522+ // A success: onHeaders (which calls onBeforeSessionStart)
523+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
524+ .start (mock (Listener .class ), new Metadata ());
525+
526+ PeerInfo peerInfo = PeerInfo .newBuilder ().setApplicationFrontendId (555 ).build ();
527+ Metadata headers = new Metadata ();
528+ headers .put (
529+ SessionStreamImpl .PEER_INFO_KEY ,
530+ Base64 .getEncoder ().encodeToString (peerInfo .toByteArray ()));
531+ listener .getValue ().onHeaders (headers );
532+ listener .getValue ().onClose (Status .OK , new Metadata ());
533+
534+ // Another 4 failures - should still not recycle because counter was reset
535+ for (int i = 0 ; i < 4 ; i ++) {
536+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
537+ .start (mock (Listener .class ), new Metadata ());
538+ listener .getValue ().onClose (Status .UNAVAILABLE , new Metadata ());
539+ }
540+ verify (channel , times (0 )).shutdown ();
541+
542+ pool .close ();
543+ }
544+
545+ @ Test
546+ void testCancelledDoesNotIncrementFailures () {
547+ when (channelSupplier .get ()).thenReturn (channel );
548+ when (channel .newCall (any (), any ())).thenReturn (clientCall );
549+ doNothing ().when (clientCall ).start (listener .capture (), any ());
550+
551+ ChannelPoolDpImpl pool =
552+ new ChannelPoolDpImpl (channelSupplier , defaultConfig , debugTagTracer , bgExecutor );
553+
554+ for (int i = 0 ; i < 10 ; i ++) {
555+ pool .newStream (FakeSessionGrpc .getOpenSessionMethod (), CallOptions .DEFAULT )
556+ .start (mock (Listener .class ), new Metadata ());
557+ listener .getValue ().onClose (Status .CANCELLED , new Metadata ());
558+ }
559+
560+ // Should never be recycled
561+ verify (channel , times (0 )).shutdown ();
562+ verify (channelSupplier , times (1 )).get ();
563+
564+ pool .close ();
565+ }
472566}
0 commit comments