Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed CSHARP-406. Made sure to release higher level locks before call…

…ing lower level methods and where possible moved time consuming operations outside of a lock.
  • Loading branch information...
commit 76c853c4c121dd768da3be3637e91f2ace4aa282 1 parent b4af659
rstam authored
View
32 Driver/Core/MongoServer.cs
@@ -1114,6 +1114,7 @@ public void VerifyState()
// internal methods
internal MongoConnection AcquireConnection(MongoDatabase database, bool slaveOk)
{
+ MongoConnection requestConnection = null;
lock (_serverLock)
{
// if a thread has called RequestStart it wants all operations to take place on the same connection
@@ -1125,17 +1126,24 @@ internal MongoConnection AcquireConnection(MongoDatabase database, bool slaveOk)
{
throw new InvalidOperationException("A call to AcquireConnection with slaveOk false is not allowed when the current RequestStart was made with slaveOk true.");
}
- request.Connection.CheckAuthentication(database); // will throw exception if authentication fails
- return request.Connection;
+ requestConnection = request.Connection;
}
+ }
- var serverInstance = ChooseServerInstance(slaveOk);
- return serverInstance.AcquireConnection(database);
+ // check authentication outside of lock
+ if (requestConnection != null)
+ {
+ requestConnection.CheckAuthentication(database); // will throw exception if authentication fails
+ return requestConnection;
}
+
+ var serverInstance = ChooseServerInstance(slaveOk);
+ return serverInstance.AcquireConnection(database);
}
internal MongoConnection AcquireConnection(MongoDatabase database, MongoServerInstance serverInstance)
{
+ MongoConnection requestConnection = null;
lock (_serverLock)
{
// if a thread has called RequestStart it wants all operations to take place on the same connection
@@ -1150,12 +1158,18 @@ internal MongoConnection AcquireConnection(MongoDatabase database, MongoServerIn
serverInstance.Address, request.Connection.ServerInstance.Address);
throw new MongoConnectionException(message);
}
- request.Connection.CheckAuthentication(database); // will throw exception if authentication fails
- return request.Connection;
+ requestConnection = request.Connection;
}
+ }
- return serverInstance.AcquireConnection(database);
+ // check authentication outside of lock
+ if (requestConnection != null)
+ {
+ requestConnection.CheckAuthentication(database); // will throw exception if authentication fails
+ return requestConnection;
}
+
+ return serverInstance.AcquireConnection(database);
}
internal void AddInstance(MongoServerInstance instance)
@@ -1252,9 +1266,9 @@ internal void ReleaseConnection(MongoConnection connection)
}
return; // hold on to the connection until RequestDone is called
}
-
- connection.ServerInstance.ReleaseConnection(connection);
}
+
+ connection.ServerInstance.ReleaseConnection(connection);
}
internal void RemoveInstance(MongoServerInstance instance)
View
11 Driver/Core/MongoServerInstance.cs
@@ -261,7 +261,7 @@ public void VerifyState()
}
finally
{
- ReleaseConnection(connection);
+ _connectionPool.ReleaseConnection(connection);
}
}
}
@@ -277,8 +277,8 @@ internal MongoConnection AcquireConnection(MongoDatabase database)
var message = string.Format("Server instance {0} is no longer connected.", _address);
throw new InvalidOperationException(message);
}
- connection = _connectionPool.AcquireConnection(database);
}
+ connection = _connectionPool.AcquireConnection(database);
// check authentication outside the lock because it might involve a round trip to the server
try
@@ -288,7 +288,7 @@ internal MongoConnection AcquireConnection(MongoDatabase database)
catch (MongoAuthenticationException)
{
// don't let the connection go to waste just because authentication failed
- ReleaseConnection(connection); // ReleaseConnection will reacquire the lock
+ _connectionPool.ReleaseConnection(connection);
throw;
}
@@ -368,10 +368,7 @@ internal void Disconnect()
internal void ReleaseConnection(MongoConnection connection)
{
- lock (_serverInstanceLock)
- {
- _connectionPool.ReleaseConnection(connection);
- }
+ _connectionPool.ReleaseConnection(connection);
}
internal void SetState(MongoServerState state)
View
89 Driver/Internal/MongoConnectionPool.cs
@@ -141,6 +141,7 @@ internal MongoConnection AcquireConnection(MongoDatabase database)
if (_poolSize < _server.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;
@@ -220,6 +221,7 @@ internal void EnsureMinConnectionPoolSizeWorkItem(object state)
_availableConnections.Add(connection);
_poolSize++;
added = true;
+ Monitor.Pulse(_connectionPoolLock);
}
}
@@ -256,47 +258,63 @@ internal void ReleaseConnection(MongoConnection connection)
throw new ArgumentException("The connection being released does not belong to this connection pool.", "connection");
}
- lock (_connectionPoolLock)
+ // if the connection is no longer open remove it from the pool
+ if (connection.State != MongoConnectionState.Open)
{
- // if connection is from another generation of the pool just close it
- if (connection.GenerationId != _generationId)
- {
- connection.Close();
- return;
- }
+ RemoveConnection(connection);
+ return;
+ }
- // if the connection is no longer open don't remove it from the pool
- if (connection.State != MongoConnectionState.Open)
+ // 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 > _server.Settings.MaxConnectionLifeTime)
{
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)
+ var connectionIsFromAnotherGeneration = false;
+ lock (_connectionPoolLock)
+ {
+ if (connection.GenerationId == _generationId)
{
- if (DateTime.UtcNow - connection.CreatedAt > _server.Settings.MaxConnectionLifeTime)
- {
- RemoveConnection(connection);
- return;
- }
+ connection.LastUsedAt = DateTime.UtcNow;
+ _availableConnections.Add(connection);
+ Monitor.Pulse(_connectionPoolLock);
}
+ else
+ {
+ connectionIsFromAnotherGeneration = true;
+ }
+ }
- connection.LastUsedAt = DateTime.UtcNow;
- _availableConnections.Add(connection);
- Monitor.Pulse(_connectionPoolLock);
+ // if connection is from another generation of the pool just close it
+ if (connectionIsFromAnotherGeneration)
+ {
+ connection.Close();
}
}
// private methods
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;
+ lock (_connectionPoolLock)
+ {
+ // even though we may have checked the GenerationId once before getting here it might have changed since
+ if (connection.GenerationId == _generationId)
+ {
+ _availableConnections.Remove(connection); // it might or might not be in availableConnections (but remove it if it is)
+ _poolSize -= 1;
+ _connectionsRemovedSinceLastTimerTick += 1;
+ Monitor.Pulse(_connectionPoolLock);
+ }
+ }
+
+ // close connection outside of lock
connection.Close();
- Monitor.Pulse(_connectionPoolLock);
}
private void TimerCallback(object state)
@@ -322,14 +340,15 @@ private void TimerCallback(object state)
// we do this even if this one instance is currently Disconnected so we can discover when a disconnected instance comes back online
_serverInstance.VerifyState();
- lock (_connectionPoolLock)
+ // note: the state could have changed to Disconnected when VerifyState was called
+ if (_serverInstance.State == MongoServerState.Disconnected)
{
- // note: the state could have changed to Disconnected when VerifyState was called
- if (_serverInstance.State == MongoServerState.Disconnected)
- {
- return;
- }
+ return;
+ }
+ MongoConnection connectionToRemove = null;
+ lock (_connectionPoolLock)
+ {
// only remove one connection per timer tick to avoid reconnection storms
if (_connectionsRemovedSinceLastTimerTick == 0)
{
@@ -351,16 +370,22 @@ private void TimerCallback(object state)
var now = DateTime.UtcNow;
if (oldestConnection != null && now > oldestConnection.CreatedAt + server.Settings.MaxConnectionLifeTime)
{
- RemoveConnection(oldestConnection);
+ connectionToRemove = oldestConnection;
}
else if (_poolSize > server.Settings.MinConnectionPoolSize && lruConnection != null && now > lruConnection.LastUsedAt + server.Settings.MaxConnectionIdleTime)
{
- RemoveConnection(lruConnection);
+ connectionToRemove = lruConnection;
}
}
_connectionsRemovedSinceLastTimerTick = 0;
}
+ // remove connection (if any) outside of lock
+ if (connectionToRemove != null)
+ {
+ RemoveConnection(connectionToRemove);
+ }
+
if (_poolSize < server.Settings.MinConnectionPoolSize)
{
ThreadPool.QueueUserWorkItem(EnsureMinConnectionPoolSizeWorkItem, _generationId);
Please sign in to comment.
Something went wrong with that request. Please try again.