Skip to content

Commit 4fdd8cc

Browse files
author
rstam
committed
CSHARP-649: Enforce MaxConnectionIdleTime and MaxConnectionLifeTime strictly.
1 parent 0f65a47 commit 4fdd8cc

12 files changed

+145
-114
lines changed

BsonUnitTests/Jira/CSharp275Tests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ public Test(string json, string iso)
7676
new Test("Mon, 1 Jan 01 11:22:33 EST", "2001-01-01T11:22:33-5:00"),
7777
new Test("Mon, 1 Jan 29 11:22:33 EST", "2029-01-01T11:22:33-5:00"),
7878
new Test("Tue, 1 Jan 30 11:22:33 EST", "2030-01-01T11:22:33-5:00"),
79-
new Test("Thu, 1 Jan 31 11:22:33 EST", "1931-01-01T11:22:33-5:00"), // starting in 2013 year 31 will become 2031 instead of 1931
80-
new Test("Fri, 1 Jan 32 11:22:33 EST", "1932-01-01T11:22:33-5:00"),
79+
new Test("Wed, 1 Jan 31 11:22:33 EST", "2031-01-01T11:22:33-5:00"),
80+
new Test("Fri, 1 Jan 32 11:22:33 EST", "1932-01-01T11:22:33-5:00"), // starting in 2014 year 32 will become 2032 instead of 1932
8181
new Test("Sun, 1 Jan 33 11:22:33 EST", "1933-01-01T11:22:33-5:00"),
8282
new Test("Fri, 1 Jan 99 11:22:33 EST", "1999-01-01T11:22:33-5:00"),
8383
// test time zones

CSharpDriver-2010.sln

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Release Notes", "Release No
3434
Release Notes\Change Log v1.6.1-Driver.txt = Release Notes\Change Log v1.6.1-Driver.txt
3535
Release Notes\Change Log v1.7-Bson.txt = Release Notes\Change Log v1.7-Bson.txt
3636
Release Notes\Change Log v1.7-Driver.txt = Release Notes\Change Log v1.7-Driver.txt
37+
Release Notes\Change Log v1.7.1-Bson.txt = Release Notes\Change Log v1.7.1-Bson.txt
38+
Release Notes\Change Log v1.7.1-Driver.txt = Release Notes\Change Log v1.7.1-Driver.txt
3739
Release Notes\Release Notes v0.11.txt = Release Notes\Release Notes v0.11.txt
3840
Release Notes\Release Notes v0.7.txt = Release Notes\Release Notes v0.7.txt
3941
Release Notes\Release Notes v0.9.txt = Release Notes\Release Notes v0.9.txt
@@ -48,6 +50,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Release Notes", "Release No
4850
Release Notes\Release Notes v1.5.md = Release Notes\Release Notes v1.5.md
4951
Release Notes\Release Notes v1.6.1.md = Release Notes\Release Notes v1.6.1.md
5052
Release Notes\Release Notes v1.6.md = Release Notes\Release Notes v1.6.md
53+
Release Notes\Release Notes v1.7.1.md = Release Notes\Release Notes v1.7.1.md
5154
Release Notes\Release Notes v1.7.md = Release Notes\Release Notes v1.7.md
5255
EndProjectSection
5356
EndProject

CSharpDriverDocs.shfbproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<FeedbackEMailAddress>craig.wilson%4010gen.com, robert%4010gen.com sridhar%4010gen.com</FeedbackEMailAddress>
4646
<FeedbackEMailLinkText>CSharp driver API doc</FeedbackEMailLinkText>
4747
<FrameworkVersion>.NET 3.5</FrameworkVersion>
48-
<HelpFileVersion>1.7.0.4714</HelpFileVersion>
48+
<HelpFileVersion>1.7.1.4791</HelpFileVersion>
4949
</PropertyGroup>
5050
<!-- There are no properties for these groups. AnyCPU needs to appear in
5151
order for Visual Studio to perform the build. The others are optional

Driver/Internal/MongoConnection.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ public int GenerationId
115115
public DateTime LastUsedAt
116116
{
117117
get { return _lastUsedAt; }
118-
internal set { _lastUsedAt = value; }
119118
}
120119

121120
/// <summary>
@@ -340,6 +339,13 @@ internal bool IsAuthenticated(MongoDatabase database)
340339
}
341340
}
342341

342+
internal bool IsExpired()
343+
{
344+
var now = DateTime.UtcNow;
345+
return now > _createdAt + _serverInstance.Server.Settings.MaxConnectionLifeTime
346+
|| now > _lastUsedAt + _serverInstance.Server.Settings.MaxConnectionIdleTime;
347+
}
348+
343349
internal void Logout(string databaseName)
344350
{
345351
if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
@@ -457,6 +463,7 @@ internal MongoReplyMessage<TDocument> ReceiveMessage<TDocument>(
457463
{
458464
using (var buffer = new BsonBuffer())
459465
{
466+
_lastUsedAt = DateTime.UtcNow;
460467
var networkStream = GetNetworkStream();
461468
var readTimeout = (int)_serverInstance.Server.Settings.SocketTimeout.TotalMilliseconds;
462469
if (readTimeout != 0)
@@ -482,6 +489,7 @@ internal WriteConcernResult SendMessage(MongoRequestMessage message, WriteConcer
482489
if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
483490
lock (_connectionLock)
484491
{
492+
_lastUsedAt = DateTime.UtcNow;
485493
_requestId = message.RequestId;
486494

487495
message.WriteToBuffer();

Driver/Internal/MongoConnectionPool.cs

Lines changed: 111 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

GlobalAssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@
3939
// You can specify all the values or you can default the Build and Revision Numbers
4040
// by using the '*' as shown below:
4141
// [assembly: AssemblyVersion("1.0.*")]
42-
[assembly: AssemblyVersion("1.7.0.4714")]
42+
[assembly: AssemblyVersion("1.7.1.4791")]

Installer/makeinstaller.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@ECHO OFF
2-
SET FullVersion=1.7.0.4714
3-
SET Version=1.7
2+
SET FullVersion=1.7.1.4791
3+
SET Version=1.7.1
44
SET Configuration=Release
55
SET SourceBase=..
66

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
BSON library changes from 1.7 to 1.7.1
2+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
C#/.NET driver changes from 1.7 to 1.7.1
2+

Release Notes/Release Notes v1.7.1.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C# Driver Version 1.7.1 Release Notes
2+
=====================================
3+
4+
This is a minor release.

0 commit comments

Comments
 (0)