@@ -269,6 +269,16 @@ private static int stateFromChannelId(int channelId) {
269269 // Clock supplier for nanoTime, injectable for testing.
270270 private Supplier <Long > nanoClock = System ::nanoTime ;
271271
272+ @ VisibleForTesting
273+ Map <Integer , Map <String , Integer >> fallbackMapForTest () {
274+ return fallbackMap ;
275+ }
276+
277+ @ VisibleForTesting
278+ int readyChannelCountForTest () {
279+ return readyChannels .get ();
280+ }
281+
272282 @ VisibleForTesting
273283 void setNanoClock (Supplier <Long > nanoClock ) {
274284 this .nanoClock = nanoClock ;
@@ -408,10 +418,7 @@ private void removeOldestChannels(int num) {
408418
409419 for (ChannelRef channelRef : channelsToRemove ) {
410420 channelRef .resetAffinityCount ();
411- channelRef .deactivate ();
412- if (channelRef .getState () == ConnectivityState .READY ) {
413- decReadyChannels (false );
414- }
421+ channelRef .deactivateAndAccountReadiness ();
415422 }
416423
417424 // Remove affinity keys mapping for the channels.
@@ -1482,6 +1489,9 @@ private class ChannelStateMonitor implements Runnable {
14821489 private long connectingStartNanos ;
14831490 private long connectedSinceNanos ;
14841491
1492+ @ GuardedBy ("channelRef" )
1493+ private boolean readyAccounted ;
1494+
14851495 private ChannelStateMonitor (ManagedChannel channel , ChannelRef channelRef ) {
14861496 this .channelRef = channelRef ;
14871497 this .channel = channel ;
@@ -1496,15 +1506,26 @@ public ConnectivityState getCurrentState() {
14961506 return currentState ;
14971507 }
14981508
1509+ private void accountReadyIfNeeded () {
1510+ if (currentState == ConnectivityState .READY && !readyAccounted ) {
1511+ readyAccounted = true ;
1512+ incReadyChannels (false );
1513+ }
1514+ }
1515+
1516+ private void unaccountReadyIfNeeded () {
1517+ if (readyAccounted ) {
1518+ readyAccounted = false ;
1519+ decReadyChannels (false );
1520+ }
1521+ }
1522+
14991523 @ Override
15001524 public void run () {
15011525 if (channel == null ) {
15021526 return ;
15031527 }
15041528
1505- // Is the channel in the pool?
1506- boolean isActive = channelRefs .contains (this .channelRef );
1507-
15081529 // Keep minSize channels always connected.
15091530 boolean requestConnection =
15101531 channelRefs .size () < minSize
@@ -1515,35 +1536,39 @@ public void run() {
15151536 .anyMatch (id -> (id == channelRef .getId ()));
15161537
15171538 ConnectivityState newState = channel .getState (requestConnection );
1518- if (logger .isLoggable (Level .FINER )) {
1519- logger .finer (
1520- log (
1521- "Channel %d state change detected: %s -> %s" ,
1522- channelRef .getId (), currentState , newState ));
1523- }
1524- if (newState == ConnectivityState .READY && currentState != ConnectivityState .READY ) {
1525- connectedSinceNanos = System .nanoTime ();
1526- if (isActive ) {
1527- incReadyChannels (true );
1528- if (connectingStartNanos > 0 ) {
1529- saveReadinessTime (System .nanoTime () - connectingStartNanos );
1539+ boolean isActive ;
1540+ synchronized (channelRef ) {
1541+ isActive = channelRef .isActive () && channelRefs .contains (channelRef );
1542+ if (logger .isLoggable (Level .FINER )) {
1543+ logger .finer (
1544+ log (
1545+ "Channel %d state change detected: %s -> %s" ,
1546+ channelRef .getId (), currentState , newState ));
1547+ }
1548+ if (newState == ConnectivityState .READY && currentState != ConnectivityState .READY ) {
1549+ connectedSinceNanos = System .nanoTime ();
1550+ if (isActive && !readyAccounted ) {
1551+ readyAccounted = true ;
1552+ incReadyChannels (true );
1553+ if (connectingStartNanos > 0 ) {
1554+ saveReadinessTime (System .nanoTime () - connectingStartNanos );
1555+ }
15301556 }
1557+ connectingStartNanos = 0 ;
15311558 }
1532- connectingStartNanos = 0 ;
1533- }
1534- if (isActive
1535- && newState != ConnectivityState .READY
1536- && currentState == ConnectivityState .READY ) {
1537- decReadyChannels (true );
1538- }
1539- if (newState == ConnectivityState .CONNECTING
1540- && currentState != ConnectivityState .CONNECTING ) {
1541- connectingStartNanos = System .nanoTime ();
1542- }
1543- if (newState != ConnectivityState .READY ) {
1544- connectedSinceNanos = 0 ;
1559+ if (newState != ConnectivityState .READY && readyAccounted ) {
1560+ readyAccounted = false ;
1561+ decReadyChannels (true );
1562+ }
1563+ if (newState == ConnectivityState .CONNECTING
1564+ && currentState != ConnectivityState .CONNECTING ) {
1565+ connectingStartNanos = System .nanoTime ();
1566+ }
1567+ if (newState != ConnectivityState .READY ) {
1568+ connectedSinceNanos = 0 ;
1569+ }
1570+ currentState = newState ;
15451571 }
1546- currentState = newState ;
15471572
15481573 processChannelStateChange (channelRef .getId (), newState );
15491574 if (isActive ) {
@@ -1686,8 +1711,12 @@ protected ChannelRef getChannelRef(@Nullable String key) {
16861711 if (logger .isLoggable (Level .FINEST )) {
16871712 logger .finest (log ("Using fallback channel: %d -> %d" , mappedChannel .getId (), channelId ));
16881713 }
1689- fallbacksSucceeded .incrementAndGet ();
1690- return channelRefs .get (channelId );
1714+ ChannelRef fallbackChannel = channelIdToChannelRef .get (channelId );
1715+ if (fallbackChannel != null && fallbackChannel .isActive ()) {
1716+ fallbacksSucceeded .incrementAndGet ();
1717+ return fallbackChannel ;
1718+ }
1719+ tempMap .remove (key , channelId );
16911720 }
16921721 // No temp mapping for this key or fallback channel is also broken.
16931722 ChannelRef channelRef = pickLeastBusyChannel (/* forFallback= */ true );
@@ -1710,7 +1739,10 @@ protected ChannelRef getChannelRef(@Nullable String key) {
17101739 fallbacksFailed .incrementAndGet ();
17111740 if (channelId != null ) {
17121741 // Stick with previous mapping if fallback has failed.
1713- return channelRefs .get (channelId );
1742+ ChannelRef fallbackChannel = channelIdToChannelRef .get (channelId );
1743+ if (fallbackChannel != null && fallbackChannel .isActive ()) {
1744+ return fallbackChannel ;
1745+ }
17141746 }
17151747 return mappedChannel ;
17161748 }
@@ -1779,16 +1811,16 @@ ChannelRef createNewChannel() {
17791811 channelRefs .add (chRef );
17801812 removedChannelRefs .remove (chRef );
17811813 channelIdToChannelRef .put (chRef .getId (), chRef );
1782- chRef .activate ();
1814+ chRef .activateAndAccountReadiness ();
17831815 logger .finer (log ("Channel %d reused." , chRef .getId ()));
1784- incReadyChannels (false );
17851816 maxChannels .accumulateAndGet (getNumberOfChannels (), Math ::max );
17861817 return chRef ;
17871818 }
17881819
17891820 ChannelRef channelRef = new ChannelRef (delegateChannelBuilder .build ());
17901821 channelRefs .add (channelRef );
17911822 channelIdToChannelRef .put (channelRef .getId (), channelRef );
1823+ channelRef .activateAndAccountReadiness ();
17921824 logger .finer (log ("Channel %d created." , channelRef .getId ()));
17931825 maxChannels .accumulateAndGet (getNumberOfChannels (), Math ::max );
17941826 return channelRef ;
@@ -2447,12 +2479,27 @@ protected boolean isActive() {
24472479 return active ;
24482480 }
24492481
2450- private void activate () {
2451- active = true ;
2482+ private void activateAndAccountReadiness () {
2483+ synchronized (this ) {
2484+ active = true ;
2485+ channelStateMonitor .accountReadyIfNeeded ();
2486+ }
2487+ }
2488+
2489+ private void deactivateAndAccountReadiness () {
2490+ synchronized (this ) {
2491+ channelStateMonitor .unaccountReadyIfNeeded ();
2492+ active = false ;
2493+ }
24522494 }
24532495
24542496 private void deactivate () {
2455- active = false ;
2497+ deactivateAndAccountReadiness ();
2498+ }
2499+
2500+ @ VisibleForTesting
2501+ void deactivateForTest () {
2502+ deactivateAndAccountReadiness ();
24562503 }
24572504
24582505 protected void affinityCountIncr () {
0 commit comments