internal void EnsureMinConnectionPoolSizeWorkItem(
            object state // forGenerationId
            )
        {
            // make sure only one instance of EnsureMinConnectionPoolSizeWorkItem is running at a time
            if (inEnsureMinConnectionPoolSizeWorkItem)
            {
                return;
            }

            inEnsureMinConnectionPoolSizeWorkItem = true;
            try {
                // keep creating connections one at a time until MinConnectionPoolSize is reached
                var forGenerationId = (int)state;
                while (true)
                {
                    lock (connectionPoolLock) {
                        // stop if the connection pool generationId has changed or we have already reached MinConnectionPoolSize
                        if (generationId != forGenerationId || poolSize >= server.Settings.MinConnectionPoolSize)
                        {
                            return;
                        }
                    }

                    var connection = new MongoConnection(this);
                    try {
                        connection.Open();

                        // compare against MaxConnectionPoolSize instead of MinConnectionPoolSize
                        // because while we were opening this connection many others may have already been created
                        // and we don't want to throw this one away unless we would exceed MaxConnectionPoolSize
                        var added = false;
                        lock (connectionPoolLock) {
                            if (generationId == forGenerationId && poolSize < server.Settings.MaxConnectionPoolSize)
                            {
                                availableConnections.Add(connection);
                                poolSize++;
                                added = true;
                            }
                        }

                        if (!added)
                        {
                            // turns out we couldn't use the connection after all
                            connection.Close();
                        }
                    } catch {
                        // TODO: log exception?
                        // wait a bit before trying again
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }
                }
            } catch {
                // don't let unhandled exceptions leave EnsureMinConnectionPoolSizeWorkItem
                // if the minimum connection pool size was not achieved a new work item will be queued shortly
                // TODO: log exception?
            } finally {
                inEnsureMinConnectionPoolSizeWorkItem = false;
            }
        }
Example #2
0
        private void Connect(
            IPEndPoint endPoint,
            TimeSpan timeout
            )
        {
            var  connection = new MongoConnection(null, endPoint); // no connection pool
            bool isPrimary;

            try {
                var isMasterCommand = new CommandDocument("ismaster", 1);
                var isMasterResult  = connection.RunCommand(server, "admin.$cmd", QueryFlags.SlaveOk, isMasterCommand);

                isPrimary = isMasterResult.Response["ismaster", false].ToBoolean();
                if (!isPrimary && !server.Settings.SlaveOk)
                {
                    throw new MongoConnectionException("Server is not a primary and SlaveOk is false");
                }

                maxDocumentSize  = isMasterResult.Response["maxBsonObjectSize", server.MaxDocumentSize].ToInt32();
                maxMessageLength = Math.Max(MongoDefaults.MaxMessageLength, maxDocumentSize + 1024); // derived from maxDocumentSize
            } catch {
                try { connection.Close(); } catch { } // ignore exceptions
                throw;
            }

            this.connection = connection;
            this.isPrimary  = isPrimary;
        }
 // 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;
     connection.Close();
     Monitor.Pulse(_connectionPoolLock);
 }
Example #4
0
        internal void MaintainPoolSize()
        {
            if (_inMaintainPoolSize)
            {
                return;
            }

            _inMaintainPoolSize = true;
            try
            {
                MongoConnection connectionToClose = null;
                lock (_connectionPoolLock)
                {
                    for (int i = 0; i < _availableConnections.Count; i++)
                    {
                        var connection = _availableConnections[i];
                        if (connection.IsExpired())
                        {
                            _availableConnections.RemoveAt(i);
                            _poolSize        -= 1;
                            connectionToClose = connection;
                            Monitor.Pulse(_connectionPoolLock);
                            break;
                        }
                    }
                }

                if (connectionToClose != null)
                {
                    try
                    {
                        connectionToClose.Close();
                    }
                    catch
                    {
                        // ignore exceptions
                    }
                }

                if (_poolSize < _settings.MinConnectionPoolSize)
                {
                    ThreadPool.QueueUserWorkItem(EnsureMinConnectionPoolSizeWorkItem, _generationId);
                }
            }
            finally
            {
                _inMaintainPoolSize = false;
            }
        }
Example #5
0
        private void RemoveConnection(MongoConnection connection)
        {
            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;
                    Monitor.Pulse(_connectionPoolLock);
                }
            }

            // close connection outside of lock
            connection.Close();
        }
Example #6
0
        internal void ReleaseConnection(MongoConnection connection)
        {
            if (connection.ConnectionPool != this)
            {
                throw new ArgumentException("The connection being released does not belong to this connection pool.", "connection");
            }

            // if the connection is no longer open remove it from the pool
            if (connection.State != MongoConnectionState.Open)
            {
                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 > _server.Settings.MaxConnectionLifeTime)
                {
                    RemoveConnection(connection);
                    return;
                }
            }

            var connectionIsFromAnotherGeneration = false;

            lock (_connectionPoolLock)
            {
                if (connection.GenerationId == _generationId)
                {
                    connection.LastUsedAt = DateTime.UtcNow;
                    _availableConnections.Add(connection);
                    Monitor.Pulse(_connectionPoolLock);
                }
                else
                {
                    connectionIsFromAnotherGeneration = true;
                }
            }

            // if connection is from another generation of the pool just close it
            if (connectionIsFromAnotherGeneration)
            {
                connection.Close();
            }
        }
Example #7
0
        // note: this method will run on a thread from the ThreadPool
        private void QueryNodeWorkItem(
            object parameters
            )
        {
            // this method has to work at a very low level because the connection pool isn't set up yet
            var args     = (QueryNodeParameters)parameters;
            var response = new QueryNodeResponse {
                Address = args.Address, EndPoint = args.EndPoint
            };

            try {
                var connection = new MongoConnection(null, args.EndPoint); // no connection pool
                try {
                    var isMasterCommand = new CommandDocument("ismaster", 1);
                    var isMasterResult  = connection.RunCommand(server, "admin.$cmd", QueryFlags.SlaveOk, isMasterCommand);

                    response.IsMasterResult   = isMasterResult;
                    response.Connection       = connection; // might become the first connection in the connection pool
                    response.IsPrimary        = isMasterResult.Response["ismaster", false].ToBoolean();
                    response.MaxDocumentSize  = isMasterResult.Response["maxBsonObjectSize", server.MaxDocumentSize].ToInt32();
                    response.MaxMessageLength = Math.Max(MongoDefaults.MaxMessageLength, response.MaxDocumentSize + 1024); // derived from maxDocumentSize

                    if (server.Settings.ReplicaSetName != null)
                    {
                        var getStatusCommand = new CommandDocument("replSetGetStatus", 1);
                        var getStatusResult  = connection.RunCommand(server, "admin.$cmd", QueryFlags.SlaveOk, getStatusCommand);

                        var replicaSetName = getStatusResult.Response["set"].AsString;
                        if (replicaSetName != server.Settings.ReplicaSetName)
                        {
                            var message = string.Format("Host {0} belongs to a different replica set: {1}", args.EndPoint, replicaSetName);
                            throw new MongoConnectionException(message);
                        }
                    }
                } catch {
                    try { connection.Close(); } catch { } // ignore exceptions
                    throw;
                }
            } catch (Exception ex) {
                response.Exception = ex;
            }

            args.ResponseQueue.Enqueue(response);
        }
Example #8
0
        internal void ReleaseConnection(MongoConnection connection)
        {
            if (connection.ConnectionPool != this)
            {
                throw new ArgumentException("The connection being released does not belong to this connection pool.", "connection");
            }

            // if the connection is no longer open remove it from the pool
            if (connection.State != MongoConnectionState.Open || connection.IsExpired())
            {
                RemoveConnection(connection);
                return;
            }

            var closeConnection = false;

            lock (_connectionPoolLock)
            {
                if (connection.GenerationId == _generationId)
                {
                    if (_poolSize <= _settings.MaxConnectionPoolSize)
                    {
                        _availableConnections.Add(connection);
                    }
                    else
                    {
                        _poolSize      -= 1;
                        closeConnection = true;
                    }
                    Monitor.Pulse(_connectionPoolLock);
                }
                else
                {
                    closeConnection = true;
                }
            }

            // if connection is from another generation of the pool just close it
            if (closeConnection)
            {
                connection.Close();
            }
        }
Example #9
0
        internal MongoConnection AcquireConnection(AcquireConnectionOptions options)
        {
            MongoConnection connectionToClose = null;

            try
            {
                DateTime timeoutAt = DateTime.UtcNow + options.WaitQueueTimeout;

                lock (_connectionPoolLock)
                {
                    if (_waitQueueSize >= _settings.WaitQueueSize && !options.OkToExceedWaitQueueSize)
                    {
                        throw new MongoConnectionException("Too many threads are already waiting for a connection.");
                    }

                    _waitQueueSize += 1;
                    try
                    {
                        while (true)
                        {
                            if (_availableConnections.Count > 0)
                            {
                                var connection = _availableConnections[_availableConnections.Count - 1];
                                if (connection.IsExpired())
                                {
                                    connectionToClose = connection;
                                    connection        = new MongoConnection(this);
                                }

                                _availableConnections.RemoveAt(_availableConnections.Count - 1);
                                return(connection);
                            }

                            // avoid waiting by creating a new connection if options allow it
                            if (options.OkToAvoidWaitingByCreatingNewConnection)
                            {
                                if (_poolSize < _settings.MaxConnectionPoolSize || options.OkToExceedMaxConnectionPoolSize)
                                {
                                    // 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)
                            {
                                // other methods should call Monitor.Pulse whenever:
                                // 1. an available connection is added _availableConnections
                                // 2. the _poolSize changes
                                Monitor.Wait(_connectionPoolLock, timeRemaining);
                            }
                            else
                            {
                                if (options.OkToExceedMaxConnectionPoolSize)
                                {
                                    // 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);
                                }
                                else
                                {
                                    throw new TimeoutException("Timeout waiting for a MongoConnection.");
                                }
                            }
                        }
                    }
                    finally
                    {
                        _waitQueueSize -= 1;
                    }
                }
            }
            finally
            {
                if (connectionToClose != null)
                {
                    try
                    {
                        connectionToClose.Close();
                    }
                    catch
                    {
                        // ignore exceptions
                    }
                }
            }
        }