@@ -56,6 +56,7 @@ internal class SocketConnectivitySubchannelTransport : ISubchannelTransport, IDi
56
56
private readonly ILogger _logger ;
57
57
private readonly Subchannel _subchannel ;
58
58
private readonly TimeSpan _socketPingInterval ;
59
+ private readonly TimeSpan _socketIdleTimeout ;
59
60
private readonly Func < Socket , DnsEndPoint , CancellationToken , ValueTask > _socketConnect ;
60
61
private readonly List < ActiveStream > _activeStreams ;
61
62
private readonly Timer _socketConnectedTimer ;
@@ -64,20 +65,23 @@ internal class SocketConnectivitySubchannelTransport : ISubchannelTransport, IDi
64
65
internal Socket ? _initialSocket ;
65
66
private BalancerAddress ? _initialSocketAddress ;
66
67
private List < ReadOnlyMemory < byte > > ? _initialSocketData ;
68
+ private DateTime ? _initialSocketCreatedTime ;
67
69
private bool _disposed ;
68
70
private BalancerAddress ? _currentAddress ;
69
71
70
72
public SocketConnectivitySubchannelTransport (
71
73
Subchannel subchannel ,
72
74
TimeSpan socketPingInterval ,
73
75
TimeSpan ? connectTimeout ,
76
+ TimeSpan socketIdleTimeout ,
74
77
ILoggerFactory loggerFactory ,
75
78
Func < Socket , DnsEndPoint , CancellationToken , ValueTask > ? socketConnect )
76
79
{
77
80
_logger = loggerFactory . CreateLogger < SocketConnectivitySubchannelTransport > ( ) ;
78
81
_subchannel = subchannel ;
79
82
_socketPingInterval = socketPingInterval ;
80
83
ConnectTimeout = connectTimeout ;
84
+ _socketIdleTimeout = socketIdleTimeout ;
81
85
_socketConnect = socketConnect ?? OnConnect ;
82
86
_activeStreams = new List < ActiveStream > ( ) ;
83
87
_socketConnectedTimer = NonCapturingTimer . Create ( OnCheckSocketConnection , state : null , Timeout . InfiniteTimeSpan , Timeout . InfiniteTimeSpan ) ;
@@ -125,6 +129,7 @@ private void DisconnectUnsynchronized()
125
129
_initialSocket = null ;
126
130
_initialSocketAddress = null ;
127
131
_initialSocketData = null ;
132
+ _initialSocketCreatedTime = null ;
128
133
_lastEndPointIndex = 0 ;
129
134
_currentAddress = null ;
130
135
}
@@ -162,6 +167,7 @@ public async ValueTask<ConnectResult> TryConnectAsync(ConnectContext context)
162
167
_initialSocket = socket ;
163
168
_initialSocketAddress = currentAddress ;
164
169
_initialSocketData = null ;
170
+ _initialSocketCreatedTime = DateTime . UtcNow ;
165
171
166
172
// Schedule ping. Don't set a periodic interval to avoid any chance of timer causing the target method to run multiple times in paralle.
167
173
// This could happen because of execution delays (e.g. hitting a debugger breakpoint).
@@ -338,6 +344,7 @@ public async ValueTask<Stream> GetStreamAsync(BalancerAddress address, Cancellat
338
344
Socket ? socket = null ;
339
345
BalancerAddress ? socketAddress = null ;
340
346
List < ReadOnlyMemory < byte > > ? socketData = null ;
347
+ DateTime ? socketCreatedTime = null ;
341
348
lock ( Lock )
342
349
{
343
350
if ( _initialSocket != null )
@@ -347,9 +354,11 @@ public async ValueTask<Stream> GetStreamAsync(BalancerAddress address, Cancellat
347
354
socket = _initialSocket ;
348
355
socketAddress = _initialSocketAddress ;
349
356
socketData = _initialSocketData ;
357
+ socketCreatedTime = _initialSocketCreatedTime ;
350
358
_initialSocket = null ;
351
359
_initialSocketAddress = null ;
352
360
_initialSocketData = null ;
361
+ _initialSocketCreatedTime = null ;
353
362
354
363
// Double check the address matches the socket address and only use socket on match.
355
364
// Not sure if this is possible in practice, but better safe than sorry.
@@ -365,10 +374,23 @@ public async ValueTask<Stream> GetStreamAsync(BalancerAddress address, Cancellat
365
374
366
375
if ( socket != null )
367
376
{
368
- if ( IsSocketInBadState ( socket , address ) )
377
+ Debug . Assert ( socketCreatedTime != null ) ;
378
+
379
+ var closeSocket = false ;
380
+
381
+ if ( DateTime . UtcNow > socketCreatedTime . Value . Add ( _socketIdleTimeout ) )
382
+ {
383
+ SocketConnectivitySubchannelTransportLog . ClosingSocketFromIdleTimeoutOnCreateStream ( _logger , _subchannel . Id , address , _socketIdleTimeout ) ;
384
+ closeSocket = true ;
385
+ }
386
+ else if ( IsSocketInBadState ( socket , address ) )
369
387
{
370
388
SocketConnectivitySubchannelTransportLog . ClosingUnusableSocketOnCreateStream ( _logger , _subchannel . Id , address ) ;
389
+ closeSocket = true ;
390
+ }
371
391
392
+ if ( closeSocket )
393
+ {
372
394
socket . Dispose ( ) ;
373
395
socket = null ;
374
396
socketData = null ;
@@ -530,6 +552,9 @@ internal static class SocketConnectivitySubchannelTransportLog
530
552
private static readonly Action < ILogger , int , BalancerAddress , Exception ? > _closingUnusableSocketOnCreateStream =
531
553
LoggerMessage . Define < int , BalancerAddress > ( LogLevel . Debug , new EventId ( 16 , "ClosingUnusableSocketOnCreateStream" ) , "Subchannel id '{SubchannelId}' socket {Address} is being closed because it can't be used. The socket either can't receive data or it has received unexpected data." ) ;
532
554
555
+ private static readonly Action < ILogger , int , BalancerAddress , TimeSpan , Exception ? > _closingSocketFromIdleTimeoutOnCreateStream =
556
+ LoggerMessage . Define < int , BalancerAddress , TimeSpan > ( LogLevel . Debug , new EventId ( 16 , "ClosingSocketFromIdleTimeoutOnCreateStream" ) , "Subchannel id '{SubchannelId}' socket {Address} is being closed because it exceeds the idle timeout of {SocketIdleTimeout}." ) ;
557
+
533
558
public static void ConnectingSocket ( ILogger logger , int subchannelId , BalancerAddress address )
534
559
{
535
560
_connectingSocket ( logger , subchannelId , address , null ) ;
@@ -609,5 +634,10 @@ public static void ClosingUnusableSocketOnCreateStream(ILogger logger, int subch
609
634
{
610
635
_closingUnusableSocketOnCreateStream ( logger , subchannelId , address , null ) ;
611
636
}
637
+
638
+ public static void ClosingSocketFromIdleTimeoutOnCreateStream ( ILogger logger , int subchannelId , BalancerAddress address , TimeSpan socketIdleTimeout )
639
+ {
640
+ _closingSocketFromIdleTimeoutOnCreateStream ( logger , subchannelId , address , socketIdleTimeout , null ) ;
641
+ }
612
642
}
613
643
#endif
0 commit comments