@@ -37,7 +37,6 @@ public class MongoConnectionPool
3737 private int _waitQueueSize ;
3838 private bool _inMaintainPoolSize ;
3939 private bool _inEnsureMinConnectionPoolSizeWorkItem ;
40- private int _connectionsRemovedSinceLastTimerTick ;
4140
4241 // constructors
4342 internal MongoConnectionPool ( MongoServerInstance serverInstance )
@@ -88,75 +87,104 @@ internal MongoConnection AcquireConnection(MongoDatabase database)
8887 throw new ArgumentException ( "This connection pool is for a different server." , "database" ) ;
8988 }
9089
91- lock ( _connectionPoolLock )
90+ MongoConnection connectionToClose = null ;
91+ try
9292 {
93- if ( _waitQueueSize >= _server . Settings . WaitQueueSize )
94- {
95- throw new MongoConnectionException ( "Too many threads are already waiting for a connection." ) ;
96- }
93+ DateTime timeoutAt = DateTime . UtcNow + _server . Settings . WaitQueueTimeout ;
9794
98- _waitQueueSize += 1 ;
99- try
95+ lock ( _connectionPoolLock )
10096 {
101- DateTime timeoutAt = DateTime . UtcNow + _server . Settings . WaitQueueTimeout ;
102- while ( true )
97+ if ( _waitQueueSize >= _server . Settings . WaitQueueSize )
10398 {
104- if ( _availableConnections . Count > 0 )
99+ throw new MongoConnectionException ( "Too many threads are already waiting for a connection." ) ;
100+ }
101+
102+ _waitQueueSize += 1 ;
103+ try
104+ {
105+ while ( true )
105106 {
106- // first try to find the most recently used connection that is already authenticated for this database
107- for ( int i = _availableConnections . Count - 1 ; i >= 0 ; i -- )
107+ if ( _availableConnections . Count > 0 )
108108 {
109- if ( _availableConnections [ i ] . IsAuthenticated ( database ) )
109+ // first try to find the most recently used connection that is already authenticated for this database
110+ for ( int i = _availableConnections . Count - 1 ; i >= 0 ; i -- )
110111 {
111- var connection = _availableConnections [ i ] ;
112- _availableConnections . RemoveAt ( i ) ;
113- return connection ;
112+ if ( _availableConnections [ i ] . IsAuthenticated ( database ) )
113+ {
114+ var connection = _availableConnections [ i ] ;
115+ _availableConnections . RemoveAt ( i ) ;
116+ if ( connection . IsExpired ( ) )
117+ {
118+ connectionToClose = connection ;
119+ connection = new MongoConnection ( this ) ;
120+ }
121+ return connection ;
122+ }
114123 }
115- }
116124
117- // otherwise find the most recently used connection that can be authenticated for this database
118- for ( int i = _availableConnections . Count - 1 ; i >= 0 ; i -- )
119- {
120- if ( _availableConnections [ i ] . CanAuthenticate ( database ) )
125+ // otherwise find the most recently used connection that can be authenticated for this database
126+ for ( int i = _availableConnections . Count - 1 ; i >= 0 ; i -- )
121127 {
122- var connection = _availableConnections [ i ] ;
123- _availableConnections . RemoveAt ( i ) ;
124- return connection ;
128+ if ( _availableConnections [ i ] . CanAuthenticate ( database ) )
129+ {
130+ var connection = _availableConnections [ i ] ;
131+ _availableConnections . RemoveAt ( i ) ;
132+ if ( connection . IsExpired ( ) )
133+ {
134+ connectionToClose = connection ;
135+ connection = new MongoConnection ( this ) ;
136+ }
137+ return connection ;
138+ }
125139 }
126- }
127140
128- // otherwise replace the least recently used connection with a brand new one
129- // if this happens a lot the connection pool size should be increased
130- _availableConnections [ 0 ] . Close ( ) ;
131- _availableConnections . RemoveAt ( 0 ) ;
132- return new MongoConnection ( this ) ;
133- }
141+ // otherwise replace the least recently used connection with a brand new one
142+ // if this happens a lot the connection pool size should be increased
143+ _availableConnections [ 0 ] . Close ( ) ;
144+ _availableConnections . RemoveAt ( 0 ) ;
145+ return new MongoConnection ( this ) ;
146+ }
134147
135- // create a new connection if maximum pool size has not been reached
136- if ( _poolSize < _server . Settings . MaxConnectionPoolSize )
137- {
138- // make sure connection is created successfully before incrementing poolSize
139- // connection will be opened later outside of the lock
140- var connection = new MongoConnection ( this ) ;
141- _poolSize += 1 ;
142- return connection ;
143- }
148+ // create a new connection if maximum pool size has not been reached
149+ if ( _poolSize < _server . Settings . MaxConnectionPoolSize )
150+ {
151+ // make sure connection is created successfully before incrementing poolSize
152+ // connection will be opened later outside of the lock
153+ var connection = new MongoConnection ( this ) ;
154+ _poolSize += 1 ;
155+ return connection ;
156+ }
144157
145- // wait for a connection to be released
146- var timeRemaining = timeoutAt - DateTime . UtcNow ;
147- if ( timeRemaining > TimeSpan . Zero )
148- {
149- Monitor . Wait ( _connectionPoolLock , timeRemaining ) ;
150- }
151- else
152- {
153- throw new TimeoutException ( "Timeout waiting for a MongoConnection." ) ;
158+ // wait for a connection to be released
159+ var timeRemaining = timeoutAt - DateTime . UtcNow ;
160+ if ( timeRemaining > TimeSpan . Zero )
161+ {
162+ Monitor . Wait ( _connectionPoolLock , timeRemaining ) ;
163+ }
164+ else
165+ {
166+ throw new TimeoutException ( "Timeout waiting for a MongoConnection." ) ;
167+ }
154168 }
155169 }
170+ finally
171+ {
172+ _waitQueueSize -= 1 ;
173+ }
156174 }
157- finally
175+ }
176+ finally
177+ {
178+ if ( connectionToClose != null )
158179 {
159- _waitQueueSize -= 1 ;
180+ try
181+ {
182+ connectionToClose . Close ( ) ;
183+ }
184+ catch
185+ {
186+ // ignore exceptions
187+ }
160188 }
161189 }
162190 }
@@ -186,44 +214,33 @@ internal void MaintainPoolSize()
186214 _inMaintainPoolSize = true ;
187215 try
188216 {
189- MongoConnection connectionToRemove = null ;
217+ MongoConnection connectionToClose = null ;
190218 lock ( _connectionPoolLock )
191219 {
192- // only remove one connection per timer tick to avoid reconnection storms
193- if ( _connectionsRemovedSinceLastTimerTick == 0 )
220+ for ( int i = 0 ; i < _availableConnections . Count ; i ++ )
194221 {
195- MongoConnection oldestConnection = null ;
196- MongoConnection lruConnection = null ;
197- foreach ( var connection in _availableConnections )
198- {
199- if ( oldestConnection == null || connection . CreatedAt < oldestConnection . CreatedAt )
200- {
201- oldestConnection = connection ;
202- }
203- if ( lruConnection == null || connection . LastUsedAt < lruConnection . LastUsedAt )
204- {
205- lruConnection = connection ;
206- }
207- }
208-
209- // remove old connections before idle connections
210- var now = DateTime . UtcNow ;
211- if ( oldestConnection != null && now > oldestConnection . CreatedAt + _server . Settings . MaxConnectionLifeTime )
212- {
213- connectionToRemove = oldestConnection ;
214- }
215- else if ( _poolSize > _server . Settings . MinConnectionPoolSize && lruConnection != null && now > lruConnection . LastUsedAt + _server . Settings . MaxConnectionIdleTime )
222+ var connection = _availableConnections [ i ] ;
223+ if ( connection . IsExpired ( ) )
216224 {
217- connectionToRemove = lruConnection ;
225+ _availableConnections . RemoveAt ( i ) ;
226+ _poolSize -= 1 ;
227+ connectionToClose = connection ;
228+ Monitor . Pulse ( _connectionPoolLock ) ;
229+ break ;
218230 }
219231 }
220- _connectionsRemovedSinceLastTimerTick = 0 ;
221232 }
222233
223- // remove connection (if any) outside of lock
224- if ( connectionToRemove != null )
234+ if ( connectionToClose != null )
225235 {
226- RemoveConnection ( connectionToRemove ) ;
236+ try
237+ {
238+ connectionToClose . Close ( ) ;
239+ }
240+ catch
241+ {
242+ // ignore exceptions
243+ }
227244 }
228245
229246 if ( _poolSize < _server . Settings . MinConnectionPoolSize )
@@ -245,40 +262,36 @@ internal void ReleaseConnection(MongoConnection connection)
245262 }
246263
247264 // if the connection is no longer open remove it from the pool
248- if ( connection . State != MongoConnectionState . Open )
265+ if ( connection . State != MongoConnectionState . Open || connection . IsExpired ( ) )
249266 {
250267 RemoveConnection ( connection ) ;
251268 return ;
252269 }
253270
254- // don't put connections that have reached their maximum lifetime back in the pool
255- // but only remove one connection at most per timer tick to avoid connection storms
256- if ( _connectionsRemovedSinceLastTimerTick == 0 )
257- {
258- if ( DateTime . UtcNow - connection . CreatedAt > _server . Settings . MaxConnectionLifeTime )
259- {
260- RemoveConnection ( connection ) ;
261- return ;
262- }
263- }
264-
265- var connectionIsFromAnotherGeneration = false ;
271+ var closeConnection = false ;
266272 lock ( _connectionPoolLock )
267273 {
268274 if ( connection . GenerationId == _generationId )
269275 {
270- connection . LastUsedAt = DateTime . UtcNow ;
271- _availableConnections . Add ( connection ) ;
276+ if ( _poolSize <= _server . Settings . MaxConnectionPoolSize )
277+ {
278+ _availableConnections . Add ( connection ) ;
279+ }
280+ else
281+ {
282+ _poolSize -= 1 ;
283+ closeConnection = true ;
284+ }
272285 Monitor . Pulse ( _connectionPoolLock ) ;
273286 }
274287 else
275288 {
276- connectionIsFromAnotherGeneration = true ;
289+ closeConnection = true ;
277290 }
278291 }
279292
280293 // if connection is from another generation of the pool just close it
281- if ( connectionIsFromAnotherGeneration )
294+ if ( closeConnection )
282295 {
283296 connection . Close ( ) ;
284297 }
@@ -364,7 +377,6 @@ private void RemoveConnection(MongoConnection connection)
364377 {
365378 _availableConnections . Remove ( connection ) ; // it might or might not be in availableConnections (but remove it if it is)
366379 _poolSize -= 1 ;
367- _connectionsRemovedSinceLastTimerTick += 1 ;
368380 Monitor . Pulse ( _connectionPoolLock ) ;
369381 }
370382 }
0 commit comments