Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

CSHARP-649: MaxConnectionLifeTime and MaxConnectionIdleTime are now s…

…trictly enforced.
  • Loading branch information...
commit 275bf49fd12b2f5d360d7095fac2eff257b4079b 1 parent f7bdc76
Craig Wilson craiggwilson authored
9 MongoDB.Driver/Communication/MongoConnection.cs
View
@@ -339,6 +339,13 @@ internal bool IsAuthenticated(string databaseName, MongoCredentials credentials)
}
}
+ internal bool IsExpired()
+ {
+ var now = DateTime.UtcNow;
+ return now > _createdAt + _serverInstance.Settings.MaxConnectionLifeTime
+ || now > _lastUsedAt + _serverInstance.Settings.MaxConnectionIdleTime;
+ }
+
internal void Logout(string databaseName)
{
if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
@@ -454,6 +461,7 @@ internal void Open()
{
try
{
+ _lastUsedAt = DateTime.UtcNow;
using (var buffer = new BsonBuffer())
{
var networkStream = GetNetworkStream();
@@ -481,6 +489,7 @@ internal WriteConcernResult SendMessage(MongoRequestMessage message, WriteConcer
if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
lock (_connectionLock)
{
+ _lastUsedAt = DateTime.UtcNow;
_requestId = message.RequestId;
message.WriteToBuffer();
188 MongoDB.Driver/Communication/MongoConnectionPool.cs
View
@@ -37,7 +37,6 @@ public class MongoConnectionPool
private int _waitQueueSize;
private bool _inMaintainPoolSize;
private bool _inEnsureMinConnectionPoolSizeWorkItem;
- private int _connectionsRemovedSinceLastTimerTick;
// constructors
internal MongoConnectionPool(MongoServerInstance serverInstance)
@@ -83,75 +82,102 @@ public MongoServerInstance ServerInstance
// internal methods
internal MongoConnection AcquireConnection(string databaseName, MongoCredentials credentials)
{
- lock (_connectionPoolLock)
+ MongoConnection connectionToClose = null;
+ try
{
- if (_waitQueueSize >= _settings.WaitQueueSize)
+ lock (_connectionPoolLock)
{
- throw new MongoConnectionException("Too many threads are already waiting for a connection.");
- }
+ if (_waitQueueSize >= _settings.WaitQueueSize)
+ {
+ throw new MongoConnectionException("Too many threads are already waiting for a connection.");
+ }
- _waitQueueSize += 1;
- try
- {
- DateTime timeoutAt = DateTime.UtcNow + _settings.WaitQueueTimeout;
- while (true)
+ _waitQueueSize += 1;
+ try
{
- if (_availableConnections.Count > 0)
+ DateTime timeoutAt = DateTime.UtcNow + _settings.WaitQueueTimeout;
+ while (true)
{
- // first try to find the most recently used connection that is already authenticated for this database
- for (int i = _availableConnections.Count - 1; i >= 0; i--)
+ if (_availableConnections.Count > 0)
{
- if (_availableConnections[i].IsAuthenticated(databaseName, credentials))
+ // first try to find the most recently used connection that is already authenticated for this database
+ for (int i = _availableConnections.Count - 1; i >= 0; i--)
{
var connection = _availableConnections[i];
- _availableConnections.RemoveAt(i);
- return connection;
+ if (connection.IsExpired())
+ {
+ _availableConnections.RemoveAt(i);
+ connectionToClose = connection;
+ return new MongoConnection(this);
+ }
+ else if (connection.IsAuthenticated(databaseName, credentials))
+ {
+ _availableConnections.RemoveAt(i);
+ return connection;
+ }
}
- }
- // otherwise find the most recently used connection that can be authenticated for this database
- for (int i = _availableConnections.Count - 1; i >= 0; i--)
- {
- if (_availableConnections[i].CanAuthenticate(databaseName, credentials))
+ // otherwise find the most recently used connection that can be authenticated for this database
+ for (int i = _availableConnections.Count - 1; i >= 0; i--)
{
var connection = _availableConnections[i];
- _availableConnections.RemoveAt(i);
- return connection;
+ if (connection.CanAuthenticate(databaseName, credentials))
+ {
+ _availableConnections.RemoveAt(i);
+ return connection;
+ }
}
- }
- // otherwise replace the least recently used connection with a brand new one
- // if this happens a lot the connection pool size should be increased
- _availableConnections[0].Close();
- _availableConnections.RemoveAt(0);
- return new MongoConnection(this);
- }
+ // otherwise replace the least recently used connection with a brand new one
+ // if this happens a lot the connection pool size should be increased
+ connectionToClose = _availableConnections[0];
+ _availableConnections.RemoveAt(0);
+ return new MongoConnection(this);
+ }
- // create a new connection if maximum pool size has not been reached
- if (_poolSize < _settings.MaxConnectionPoolSize)
- {
- // make sure connection is created successfully before incrementing poolSize
- // connection will be opened later outside of the lock
- var connection = new MongoConnection(this);
- _poolSize += 1;
- return connection;
- }
+ // create a new connection if maximum pool size has not been reached
+ if (_poolSize < _settings.MaxConnectionPoolSize)
+ {
+ // make sure connection is created successfully before incrementing poolSize
+ // connection will be opened later outside of the lock
+ var connection = new MongoConnection(this);
+ _poolSize += 1;
+ return connection;
+ }
- // wait for a connection to be released
- var timeRemaining = timeoutAt - DateTime.UtcNow;
- if (timeRemaining > TimeSpan.Zero)
- {
- Monitor.Wait(_connectionPoolLock, timeRemaining);
- }
- else
- {
- throw new TimeoutException("Timeout waiting for a MongoConnection.");
+ // wait for a connection to be released
+ var timeRemaining = timeoutAt - DateTime.UtcNow;
+ if (timeRemaining > TimeSpan.Zero)
+ {
+ // other methods should call Monitor.Pulse whenever:
+ // 1. an available connection is added _availableConnections
+ // 2. the _poolSize changes
+ Monitor.Wait(_connectionPoolLock, timeRemaining);
+ }
+ else
+ {
+ throw new TimeoutException("Timeout waiting for a MongoConnection.");
+ }
}
}
+ finally
+ {
+ _waitQueueSize -= 1;
+ }
}
- finally
+ }
+ finally
+ {
+ if (connectionToClose != null)
{
- _waitQueueSize -= 1;
+ try
+ {
+ connectionToClose.Close();
+ }
+ catch
+ {
+ // ignore exceptions
+ }
}
}
}
@@ -181,44 +207,33 @@ internal void MaintainPoolSize()
_inMaintainPoolSize = true;
try
{
- MongoConnection connectionToRemove = null;
+ MongoConnection connectionToClose = null;
lock (_connectionPoolLock)
{
- // only remove one connection per timer tick to avoid reconnection storms
- if (_connectionsRemovedSinceLastTimerTick == 0)
+ for (int i = 0; i < _availableConnections.Count; i++)
{
- MongoConnection oldestConnection = null;
- MongoConnection lruConnection = null;
- foreach (var connection in _availableConnections)
- {
- if (oldestConnection == null || connection.CreatedAt < oldestConnection.CreatedAt)
- {
- oldestConnection = connection;
- }
- if (lruConnection == null || connection.LastUsedAt < lruConnection.LastUsedAt)
- {
- lruConnection = connection;
- }
- }
-
- // remove old connections before idle connections
- var now = DateTime.UtcNow;
- if (oldestConnection != null && now > oldestConnection.CreatedAt + _settings.MaxConnectionLifeTime)
+ var connection = _availableConnections[i];
+ if (connection.IsExpired())
{
- connectionToRemove = oldestConnection;
- }
- else if (_poolSize > _settings.MinConnectionPoolSize && lruConnection != null && now > lruConnection.LastUsedAt + _settings.MaxConnectionIdleTime)
- {
- connectionToRemove = lruConnection;
+ _availableConnections.RemoveAt(i);
+ _poolSize -= 1;
+ connectionToClose = connection;
+ Monitor.Pulse(_connectionPoolLock);
+ break;
}
}
- _connectionsRemovedSinceLastTimerTick = 0;
}
- // remove connection (if any) outside of lock
- if (connectionToRemove != null)
+ if (connectionToClose != null)
{
- RemoveConnection(connectionToRemove);
+ try
+ {
+ connectionToClose.Close();
+ }
+ catch
+ {
+ // ignore exceptions
+ }
}
if (_poolSize < _settings.MinConnectionPoolSize)
@@ -240,29 +255,17 @@ internal void ReleaseConnection(MongoConnection connection)
}
// if the connection is no longer open remove it from the pool
- if (connection.State != MongoConnectionState.Open)
+ if (connection.State != MongoConnectionState.Open || connection.IsExpired())
{
RemoveConnection(connection);
return;
}
- // don't put connections that have reached their maximum lifetime back in the pool
- // but only remove one connection at most per timer tick to avoid connection storms
- if (_connectionsRemovedSinceLastTimerTick == 0)
- {
- if (DateTime.UtcNow - connection.CreatedAt > _settings.MaxConnectionLifeTime)
- {
- RemoveConnection(connection);
- return;
- }
- }
-
var connectionIsFromAnotherGeneration = false;
lock (_connectionPoolLock)
{
if (connection.GenerationId == _generationId)
{
- connection.LastUsedAt = DateTime.UtcNow;
_availableConnections.Add(connection);
Monitor.Pulse(_connectionPoolLock);
}
@@ -359,7 +362,6 @@ private void RemoveConnection(MongoConnection connection)
{
_availableConnections.Remove(connection); // it might or might not be in availableConnections (but remove it if it is)
_poolSize -= 1;
- _connectionsRemovedSinceLastTimerTick += 1;
Monitor.Pulse(_connectionPoolLock);
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.