@@ -37,7 +37,6 @@ public class MongoConnectionPool
37
37
private int _waitQueueSize ;
38
38
private bool _inMaintainPoolSize ;
39
39
private bool _inEnsureMinConnectionPoolSizeWorkItem ;
40
- private int _connectionsRemovedSinceLastTimerTick ;
41
40
42
41
// constructors
43
42
internal MongoConnectionPool ( MongoServerInstance serverInstance )
@@ -88,75 +87,104 @@ internal MongoConnection AcquireConnection(MongoDatabase database)
88
87
throw new ArgumentException ( "This connection pool is for a different server." , "database" ) ;
89
88
}
90
89
91
- lock ( _connectionPoolLock )
90
+ MongoConnection connectionToClose = null ;
91
+ try
92
92
{
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 ;
97
94
98
- _waitQueueSize += 1 ;
99
- try
95
+ lock ( _connectionPoolLock )
100
96
{
101
- DateTime timeoutAt = DateTime . UtcNow + _server . Settings . WaitQueueTimeout ;
102
- while ( true )
97
+ if ( _waitQueueSize >= _server . Settings . WaitQueueSize )
103
98
{
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 )
105
106
{
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 )
108
108
{
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 -- )
110
111
{
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
+ }
114
123
}
115
- }
116
124
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 -- )
121
127
{
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
+ }
125
139
}
126
- }
127
140
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
+ }
134
147
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
+ }
144
157
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
+ }
154
168
}
155
169
}
170
+ finally
171
+ {
172
+ _waitQueueSize -= 1 ;
173
+ }
156
174
}
157
- finally
175
+ }
176
+ finally
177
+ {
178
+ if ( connectionToClose != null )
158
179
{
159
- _waitQueueSize -= 1 ;
180
+ try
181
+ {
182
+ connectionToClose . Close ( ) ;
183
+ }
184
+ catch
185
+ {
186
+ // ignore exceptions
187
+ }
160
188
}
161
189
}
162
190
}
@@ -186,44 +214,33 @@ internal void MaintainPoolSize()
186
214
_inMaintainPoolSize = true ;
187
215
try
188
216
{
189
- MongoConnection connectionToRemove = null ;
217
+ MongoConnection connectionToClose = null ;
190
218
lock ( _connectionPoolLock )
191
219
{
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 ++ )
194
221
{
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 ( ) )
216
224
{
217
- connectionToRemove = lruConnection ;
225
+ _availableConnections . RemoveAt ( i ) ;
226
+ _poolSize -= 1 ;
227
+ connectionToClose = connection ;
228
+ Monitor . Pulse ( _connectionPoolLock ) ;
229
+ break ;
218
230
}
219
231
}
220
- _connectionsRemovedSinceLastTimerTick = 0 ;
221
232
}
222
233
223
- // remove connection (if any) outside of lock
224
- if ( connectionToRemove != null )
234
+ if ( connectionToClose != null )
225
235
{
226
- RemoveConnection ( connectionToRemove ) ;
236
+ try
237
+ {
238
+ connectionToClose . Close ( ) ;
239
+ }
240
+ catch
241
+ {
242
+ // ignore exceptions
243
+ }
227
244
}
228
245
229
246
if ( _poolSize < _server . Settings . MinConnectionPoolSize )
@@ -245,40 +262,36 @@ internal void ReleaseConnection(MongoConnection connection)
245
262
}
246
263
247
264
// 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 ( ) )
249
266
{
250
267
RemoveConnection ( connection ) ;
251
268
return ;
252
269
}
253
270
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 ;
266
272
lock ( _connectionPoolLock )
267
273
{
268
274
if ( connection . GenerationId == _generationId )
269
275
{
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
+ }
272
285
Monitor . Pulse ( _connectionPoolLock ) ;
273
286
}
274
287
else
275
288
{
276
- connectionIsFromAnotherGeneration = true ;
289
+ closeConnection = true ;
277
290
}
278
291
}
279
292
280
293
// if connection is from another generation of the pool just close it
281
- if ( connectionIsFromAnotherGeneration )
294
+ if ( closeConnection )
282
295
{
283
296
connection . Close ( ) ;
284
297
}
@@ -364,7 +377,6 @@ private void RemoveConnection(MongoConnection connection)
364
377
{
365
378
_availableConnections . Remove ( connection ) ; // it might or might not be in availableConnections (but remove it if it is)
366
379
_poolSize -= 1 ;
367
- _connectionsRemovedSinceLastTimerTick += 1 ;
368
380
Monitor . Pulse ( _connectionPoolLock ) ;
369
381
}
370
382
}
0 commit comments