コード例 #1
0
ファイル: shardconnection.cpp プロジェクト: acruikshank/mongo
        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                Shard& shard = all[i];
                try {
                    string sconnString = shard.getConnString();
                    Status* s = _getStatus( sconnString );

                    if( ! s->avail ) {
                        s->avail = shardConnectionPool.get( sconnString );
                        s->created++; // After, so failed creation doesn't get counted
                    }

                    versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
                }
                catch ( const std::exception& e ) {

                    warning() << "problem while initially checking shard versions on"
                              << " " << shard.getName() << causedBy(e) << endl;
                    throw;
                }
            }
        }
コード例 #2
0
ファイル: shardconnection.cpp プロジェクト: ANTco/mongo
        DBClientBase * get( const string& addr , const string& ns ) {
            _check( ns );

            Status* s = _getStatus( addr );

            auto_ptr<DBClientBase> c; // Handles cleanup if there's an exception thrown
            if ( s->avail ) {
                c.reset( s->avail );
                s->avail = 0;
                shardConnectionPool.onHandedOut( c.get() ); // May throw an exception
            } else {
                c.reset( shardConnectionPool.get( addr ) );
                s->created++; // After, so failed creation doesn't get counted
            }
            return c.release();
        }
コード例 #3
0
ファイル: shardconnection.cpp プロジェクト: ANTco/mongo
        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Don't report exceptions here as errors in GetLastError
            LastError::Disabled ignoreForGLE(lastError.get(false));

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                Shard& shard = all[i];
                try {
                    string sconnString = shard.getConnString();
                    Status* s = _getStatus( sconnString );

                    if( ! s->avail ) {
                        s->avail = shardConnectionPool.get( sconnString );
                        s->created++; // After, so failed creation doesn't get counted
                    }

                    versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
                }
                catch ( const DBException& ex ) {

                    warning() << "problem while initially checking shard versions on" << " "
                              << shard.getName() << causedBy( ex ) << endl;

                    // NOTE: This is only a heuristic, to avoid multiple stale version retries
                    // across multiple shards, and does not affect correctness.
                }
            }
        }
コード例 #4
0
ファイル: shardconnection.cpp プロジェクト: ANTco/mongo
 virtual bool run( OperationContext* txn, const string&, mongo::BSONObj&, int, std::string&, mongo::BSONObjBuilder& result, bool ) {
     // Base pool info
     shardConnectionPool.appendInfo( result );
     // Thread connection info
     activeClientConnections.appendInfo( result );
     return true;
 }
コード例 #5
0
ファイル: shardconnection.cpp プロジェクト: Bamco/mongo
        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                Shard& shard = all[i];
                try {
                    string sconnString = shard.getConnString();
                    Status* &s = _hosts[sconnString];

                    if ( ! s ){
                        s = new Status();
                    }

                    if( ! s->avail )
                        s->avail = shardConnectionPool.get( sconnString );

                    versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
                } catch(...) { 
                    LOGATMOST(2) << "exception in checkAllVersions shard:" << shard.getName() << endl;
                    throw;
                }
            }
        }
コード例 #6
0
ファイル: shardconnection.cpp プロジェクト: humcycles/mongo
        DBClientBase * get( const string& addr , const string& ns, bool ignoreDirect = false ) {
            _check( ns );

            // Determine if non-shard conn is RS member for warning
            // All shards added to _hosts if not present in _check()
            if( ( logLevel >= 1 || ! printedShardConnWarning ) && ! ignoreDirect && _hosts.find( addr ) == _hosts.end() ){

                vector<Shard> all;
                Shard::getAllShards( all );

                bool isRSMember = false;
                string parentShard;
                for ( unsigned i = 0; i < all.size(); i++ ) {
                    string connString = all[i].getConnString();
                    if( connString.find( addr ) != string::npos && connString.find( '/' ) != string::npos ){
                        isRSMember = true;
                        parentShard = connString;
                        break;
                    }
                }

                if( isRSMember ){
                    printedShardConnWarning = true;
                    warning() << "adding shard sub-connection " << addr << " (parent " << parentShard << ") as sharded, this is safe but unexpected" << endl;
                    printStackTrace();
                }
            }


            Status* &s = _hosts[addr];
            if ( ! s )
                s = new Status();

            if ( s->avail ) {
                DBClientBase* c = s->avail;
                s->avail = 0;
                shardConnectionPool.onHandedOut( c );
                return c;
            }

            s->created++;
            return shardConnectionPool.get( addr );
        }
コード例 #7
0
ファイル: shardconnection.cpp プロジェクト: Bamco/mongo
        DBClientBase * get( const string& addr , const string& ns ) {
            _check( ns );

            Status* &s = _hosts[addr];
            if ( ! s )
                s = new Status();

            auto_ptr<DBClientBase> c; // Handles cleanup if there's an exception thrown
            if ( s->avail ) {
                c.reset( s->avail );
                s->avail = 0;
                shardConnectionPool.onHandedOut( c.get() ); // May throw an exception
            } else {
                s->created++;
                c.reset( shardConnectionPool.get( addr ) );
            }
            if ( !noauth ) {
                c->setAuthenticationTable( ClientBasic::getCurrent()->getAuthenticationInfo()->
                                           getAuthTable() );
            }
            return c.release();
        }
コード例 #8
0
        DBClientBase * get( const string& addr , const string& ns ) {
            _check( ns );

            Status* &s = _hosts[addr];
            if ( ! s )
                s = new Status();

            if ( s->avail ) {
                DBClientBase* c = s->avail;
                s->avail = 0;
                try {
                    shardConnectionPool.onHandedOut( c );
                }
                catch ( std::exception& e ) {
                    delete c;
                    throw;
                }
                return c;
            }

            s->created++;
            return shardConnectionPool.get( addr );
        }
コード例 #9
0
ファイル: connpool.cpp プロジェクト: AllenDou/mongo
    bool DBConnectionPool::isConnectionGood(const string& hostName, DBClientBase* conn) {
        if (conn == NULL) {
            return false;
        }

        if (conn->isFailed()) {
            return false;
        }

        {
            scoped_lock sl(_mutex);
            PoolForHost& pool = _pools[PoolKey(hostName, conn->getSoTimeout())];
            if (pool.isBadSocketCreationTime(conn->getSockCreationMicroSec())) {
                return false;
            }
        }

        return true;
    }
コード例 #10
0
ファイル: shardconnection.cpp プロジェクト: ANTco/mongo
        void done( const string& addr , DBClientBase* conn ) {
            Status* s = _hosts[addr];
            verify( s );

            const bool isConnGood = shardConnectionPool.isConnectionGood(addr, conn);

            if (s->avail != NULL) {
                warning() << "Detected additional sharded connection in the "
                        "thread local pool for " << addr << endl;

                if (DBException::traceExceptions) {
                    // There shouldn't be more than one connection checked out to the same
                    // host on the same thread.
                    printStackTrace();
                }

                if (!isConnGood) {
                    delete s->avail;
                    s->avail = NULL;
                }

                // Let the internal pool handle the bad connection, this can also
                // update the lower bounds for the known good socket creation time
                // for this host.
                release(addr, conn);
                return;
            }

            if (!isConnGood) {
                // Let the internal pool handle the bad connection.
                release(addr, conn);
                return;
            }

            // Note: Although we try our best to clear bad connections as much as possible,
            // some of them can still slip through because of how ClientConnections are being
            // used - as thread local variables. This means that threads won't be able to
            // see the s->avail connection of other threads.

            s->avail = conn;
        }
コード例 #11
0
        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                string sconnString = all[i].getConnString();
                Status* &s = _hosts[sconnString];

                if ( ! s ){
                    s = new Status();
                }

                if( ! s->avail )
                    s->avail = shardConnectionPool.get( sconnString );

                versionManager.checkShardVersionCB( s->avail, ns, false, 1 );

            }
        }
コード例 #12
0
 void release( const string& addr , DBClientBase * conn ) {
     shardConnectionPool.release( addr , conn );
 }
コード例 #13
0
ファイル: shardconnection.cpp プロジェクト: humcycles/mongo
namespace mongo {

    // The code in shardconnection may run not only in mongos context. When elsewhere, chunk shard versioning
    // is disabled. To enable chunk shard versioning, provide the check/resetShardVerionCB's below
    //
    // TODO: better encapsulate this mechanism.

    bool defaultIsVersionable( DBClientBase * conn ){
        return false;
    }

    bool defaultInitShardVersion( DBClientBase & conn, BSONObj& result ){
        return false;
    }

    bool defaultForceRemoteCheckShardVersion( const string& ns ){
        return true;
    }

    bool defaultCheckShardVersion( DBClientBase & conn , const string& ns , bool authoritative , int tryNumber ) {
        // no-op in mongod
        return false;
    }

    void defaultResetShardVersion( DBClientBase * conn ) {
        // no-op in mongod
    }

    boost::function1<bool, DBClientBase* > isVersionableCB = defaultIsVersionable;
    boost::function2<bool, DBClientBase&, BSONObj& > initShardVersionCB = defaultInitShardVersion;
    boost::function1<bool, const string& > forceRemoteCheckShardVersionCB = defaultForceRemoteCheckShardVersion;
    boost::function4<bool, DBClientBase&, const string&, bool, int> checkShardVersionCB = defaultCheckShardVersion;
    boost::function1<void, DBClientBase*> resetShardVersionCB = defaultResetShardVersion;

    DBConnectionPool shardConnectionPool;

    // Only print the non-top-level-shard-conn warning once if not verbose
    volatile bool printedShardConnWarning = false;

    /**
     * holds all the actual db connections for a client to various servers
     * 1 per thread, so doesn't have to be thread safe
     */
    class ClientConnections : boost::noncopyable {
    public:
        struct Status : boost::noncopyable {
            Status() : created(0), avail(0) {}

            long long created;
            DBClientBase* avail;
        };


        ClientConnections() {}

        ~ClientConnections() {
            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                assert( ss );
                if ( ss->avail ) {
                    /* if we're shutting down, don't want to initiate release mechanism as it is slow,
                       and isn't needed since all connections will be closed anyway */
                    if ( inShutdown() ) {
                        if( isVersionableCB( ss->avail ) ) resetShardVersionCB( ss->avail );
                        delete ss->avail;
                    }
                    else
                        release( addr , ss->avail );
                    ss->avail = 0;
                }
                delete ss;
            }
            _hosts.clear();
        }

        DBClientBase * get( const string& addr , const string& ns, bool ignoreDirect = false ) {
            _check( ns );

            // Determine if non-shard conn is RS member for warning
            // All shards added to _hosts if not present in _check()
            if( ( logLevel >= 1 || ! printedShardConnWarning ) && ! ignoreDirect && _hosts.find( addr ) == _hosts.end() ){

                vector<Shard> all;
                Shard::getAllShards( all );

                bool isRSMember = false;
                string parentShard;
                for ( unsigned i = 0; i < all.size(); i++ ) {
                    string connString = all[i].getConnString();
                    if( connString.find( addr ) != string::npos && connString.find( '/' ) != string::npos ){
                        isRSMember = true;
                        parentShard = connString;
                        break;
                    }
                }

                if( isRSMember ){
                    printedShardConnWarning = true;
                    warning() << "adding shard sub-connection " << addr << " (parent " << parentShard << ") as sharded, this is safe but unexpected" << endl;
                    printStackTrace();
                }
            }


            Status* &s = _hosts[addr];
            if ( ! s )
                s = new Status();

            if ( s->avail ) {
                DBClientBase* c = s->avail;
                s->avail = 0;
                shardConnectionPool.onHandedOut( c );
                return c;
            }

            s->created++;
            return shardConnectionPool.get( addr );
        }

        void done( const string& addr , DBClientBase* conn ) {
            Status* s = _hosts[addr];
            assert( s );
            if ( s->avail ) {
                release( addr , conn );
                return;
            }
            s->avail = conn;
        }

        void sync() {
            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                if ( ss->avail )
                    ss->avail->getLastError();
                
            }
        }

        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                string sconnString = all[i].getConnString();
                Status* &s = _hosts[sconnString];

                if ( ! s ){
                    s = new Status();
                }

                if( ! s->avail )
                    s->avail = shardConnectionPool.get( sconnString );

                checkShardVersionCB( *s->avail, ns, false, 1 );

            }
        }

        void release( const string& addr , DBClientBase * conn ) {
            shardConnectionPool.release( addr , conn );
        }

        void _check( const string& ns ) {
            if ( ns.size() == 0 || _seenNS.count( ns ) )
                return;
            _seenNS.insert( ns );
            checkVersions( ns );
        }
        
        typedef map<string,Status*,DBConnectionPool::serverNameCompare> HostMap;
        HostMap _hosts;
        set<string> _seenNS;
        // -----

        static thread_specific_ptr<ClientConnections> _perThread;

        static ClientConnections* threadInstance() {
            ClientConnections* cc = _perThread.get();
            if ( ! cc ) {
                cc = new ClientConnections();
                _perThread.reset( cc );
            }
            return cc;
        }
    };

    thread_specific_ptr<ClientConnections> ClientConnections::_perThread;

    ShardConnection::ShardConnection( const Shard * s , const string& ns, bool ignoreDirect )
        : _addr( s->getConnString() ) , _ns( ns ) {
        _init( ignoreDirect );
    }

    ShardConnection::ShardConnection( const Shard& s , const string& ns, bool ignoreDirect )
        : _addr( s.getConnString() ) , _ns( ns ) {
        _init( ignoreDirect );
    }

    ShardConnection::ShardConnection( const string& addr , const string& ns, bool ignoreDirect )
        : _addr( addr ) , _ns( ns ) {
        _init( ignoreDirect );
    }

    void ShardConnection::_init( bool ignoreDirect ) {
        assert( _addr.size() );
        _conn = ClientConnections::threadInstance()->get( _addr , _ns, ignoreDirect );
        _finishedInit = false;
    }

    void ShardConnection::_finishInit() {
        if ( _finishedInit )
            return;
        _finishedInit = true;

        if ( _ns.size() && isVersionableCB( _conn ) ) {
            _setVersion = checkShardVersionCB( *_conn , _ns , false , 1 );
        }
        else {
            _setVersion = false;
        }

    }

    void ShardConnection::done() {
        if ( _conn ) {
            ClientConnections::threadInstance()->done( _addr , _conn );
            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::kill() {
        if ( _conn ) {
            if( isVersionableCB( _conn ) ) resetShardVersionCB( _conn );
            delete _conn;
            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::sync() {
        ClientConnections::threadInstance()->sync();
    }

    bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {
        assert( _conn );
        bool ok = _conn->runCommand( db , cmd , res );
        if ( ! ok ) {
            if ( res["code"].numberInt() == SendStaleConfigCode ) {
                done();
                throw RecvStaleConfigException( res["ns"].String() , res["errmsg"].String() );
            }
        }
        return ok;
    }

    void ShardConnection::checkMyConnectionVersions( const string & ns ) {
        ClientConnections::threadInstance()->checkVersions( ns );
    }

    ShardConnection::~ShardConnection() {
        if ( _conn ) {
            if ( ! _conn->isFailed() ) {
                /* see done() comments above for why we log this line */
                log() << "~ScopedDBConnection: _conn != null" << endl;
            }
            kill();
        }
    }
}
コード例 #14
0
ファイル: connpool.cpp プロジェクト: AllenDou/mongo
 void ScopedDbConnection::clearPool() {
     pool.clear();
 }
コード例 #15
0
ファイル: shardconnection.cpp プロジェクト: acruikshank/mongo
namespace mongo {

    DBConnectionPool shardConnectionPool;

    class ClientConnections;

    /**
     * Class which tracks ClientConnections (the client connection pool) for each incoming
     * connection, allowing stats access.
     */

    class ActiveClientConnections {
     public:

        ActiveClientConnections() : _mutex( "ActiveClientConnections" ) {
        }

        void add( const ClientConnections* cc ) {
            scoped_lock lock( _mutex );
            _clientConnections.insert( cc );
        }

        void remove( const ClientConnections* cc ) {
            scoped_lock lock( _mutex );
            _clientConnections.erase( cc );
        }

        // Implemented after ClientConnections
        void appendInfo( BSONObjBuilder& b );

    private:
        mongo::mutex _mutex;
        set<const ClientConnections*> _clientConnections;

    } activeClientConnections;

    /**
     * Command to allow access to the sharded conn pool information in mongos.
     * TODO: Refactor with other connection pooling changes
     */
    class ShardedPoolStats : public Command {
    public:

        ShardedPoolStats() : Command( "shardConnPoolStats" ) {}
        virtual void help( stringstream &help ) const { help << "stats about the shard connection pool"; }
        virtual LockType locktype() const { return NONE; }
        virtual bool slaveOk() const { return true; }

        // Same privs as connPoolStats
        virtual void addRequiredPrivileges( const std::string& dbname,
                                            const BSONObj& cmdObj,
                                            std::vector<Privilege>* out )
        {
            ActionSet actions;
            actions.addAction( ActionType::connPoolStats );
            out->push_back( Privilege( AuthorizationManager::SERVER_RESOURCE_NAME, actions ) );
        }

        virtual bool run ( const string&, mongo::BSONObj&, int, std::string&, mongo::BSONObjBuilder& result, bool ) {
            // Base pool info
            shardConnectionPool.appendInfo( result );
            // Thread connection info
            activeClientConnections.appendInfo( result );
            return true;
        }

    } shardedPoolStatsCmd;

    /**
     * holds all the actual db connections for a client to various servers
     * 1 per thread, so doesn't have to be thread safe
     */
    class ClientConnections : boost::noncopyable {
    public:
        struct Status : boost::noncopyable {
            Status() : created(0), avail(0) {}

            // May be read concurrently, but only written from
            // this thread.
            long long created;
            DBClientBase* avail;
        };

        // Gets or creates the status object for the host
        Status* _getStatus( const string& addr ) {
            scoped_spinlock lock( _lock );
            Status* &temp = _hosts[addr];
            if ( ! temp )
                temp = new Status();
            return temp;
        }

        ClientConnections() {
            // Start tracking client connections
            activeClientConnections.add( this );
        }

        ~ClientConnections() {
            // Stop tracking these client connections
            activeClientConnections.remove( this );

            releaseAll( true );
        }

        void releaseAll( bool fromDestructor = false ) {

            // Don't need spinlock protection because if not in the destructor, we don't 
            // modify _hosts, and if in the destructor we are not accessible to external
            // threads.

            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                verify( ss );
                if ( ss->avail ) {
                    /* if we're shutting down, don't want to initiate release mechanism as it is slow,
                       and isn't needed since all connections will be closed anyway */
                    if ( inShutdown() ) {
                        if( versionManager.isVersionableCB( ss->avail ) ) versionManager.resetShardVersionCB( ss->avail );
                        delete ss->avail;
                    }
                    else
                        release( addr , ss->avail );
                    ss->avail = 0;
                }
                if ( fromDestructor ) delete ss;
            }
            if ( fromDestructor ) _hosts.clear();
        }

        DBClientBase * get( const string& addr , const string& ns ) {
            _check( ns );

            Status* s = _getStatus( addr );

            auto_ptr<DBClientBase> c; // Handles cleanup if there's an exception thrown
            if ( s->avail ) {
                c.reset( s->avail );
                s->avail = 0;
                shardConnectionPool.onHandedOut( c.get() ); // May throw an exception
            } else {
                c.reset( shardConnectionPool.get( addr ) );
                s->created++; // After, so failed creation doesn't get counted
            }
            return c.release();
        }

        void done( const string& addr , DBClientBase* conn ) {
            Status* s = _hosts[addr];
            verify( s );

            const bool isConnGood = shardConnectionPool.isConnectionGood(addr, conn);

            if (s->avail != NULL) {
                warning() << "Detected additional sharded connection in the "
                        "thread local pool for " << addr << endl;

                if (DBException::traceExceptions) {
                    // There shouldn't be more than one connection checked out to the same
                    // host on the same thread.
                    printStackTrace();
                }

                if (!isConnGood) {
                    delete s->avail;
                    s->avail = NULL;
                }

                // Let the internal pool handle the bad connection, this can also
                // update the lower bounds for the known good socket creation time
                // for this host.
                release(addr, conn);
                return;
            }

            if (!isConnGood) {
                // Let the internal pool handle the bad connection.
                release(addr, conn);
                return;
            }

            // Note: Although we try our best to clear bad connections as much as possible,
            // some of them can still slip through because of how ClientConnections are being
            // used - as thread local variables. This means that threads won't be able to
            // see the s->avail connection of other threads.

            s->avail = conn;
        }

        void sync() {
            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                if ( ss->avail )
                    ss->avail->getLastError();
                
            }
        }

        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                Shard& shard = all[i];
                try {
                    string sconnString = shard.getConnString();
                    Status* s = _getStatus( sconnString );

                    if( ! s->avail ) {
                        s->avail = shardConnectionPool.get( sconnString );
                        s->created++; // After, so failed creation doesn't get counted
                    }

                    versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
                }
                catch ( const std::exception& e ) {

                    warning() << "problem while initially checking shard versions on"
                              << " " << shard.getName() << causedBy(e) << endl;
                    throw;
                }
            }
        }

        void release( const string& addr , DBClientBase * conn ) {
            shardConnectionPool.release( addr , conn );
        }

        void _check( const string& ns ) {

            {
                // We want to report ns stats too
                scoped_spinlock lock( _lock );
                if ( ns.size() == 0 || _seenNS.count( ns ) )
                    return;
                _seenNS.insert( ns );
            }

            checkVersions( ns );
        }
        
        /**
         * Appends info about the client connection pool to a BOBuilder
         * Safe to call with activeClientConnections lock
         */
        void appendInfo( BSONObjBuilder& b ) const {

            scoped_spinlock lock( _lock );

            BSONArrayBuilder hostsArrB( b.subarrayStart( "hosts" ) );
            for ( HostMap::const_iterator i = _hosts.begin(); i != _hosts.end(); ++i ) {
                BSONObjBuilder bb( hostsArrB.subobjStart() );
                bb.append( "host", i->first );
                bb.append( "created", i->second->created );
                bb.appendBool( "avail", static_cast<bool>( i->second->avail ) );
                bb.done();
            }
            hostsArrB.done();

            BSONArrayBuilder nsArrB( b.subarrayStart( "seenNS" ) );
            for ( set<string>::const_iterator i = _seenNS.begin(); i != _seenNS.end(); ++i ) {
                nsArrB.append(*i);
            }
            nsArrB.done();
        }
        
        // Protects only the creation of new entries in the _hosts and _seenNS map
        // from external threads.  Reading _hosts / _seenNS in this thread doesn't
        // need protection.
        mutable SpinLock _lock;
        typedef map<string,Status*,DBConnectionPool::serverNameCompare> HostMap;
        HostMap _hosts;
        set<string> _seenNS;

        /**
         * Clears the connections kept by this pool (ie, not including the global pool)
         */
        void clearPool() {
            for(HostMap::iterator iter = _hosts.begin(); iter != _hosts.end(); ++iter) {
                if (iter->second->avail != NULL) {
                    delete iter->second->avail;
                }
            }

            _hosts.clear();
        }

        void forgetNS( const string& ns ) {
            scoped_spinlock lock( _lock );
            _seenNS.erase( ns );
        }

        // -----

        static thread_specific_ptr<ClientConnections> _perThread;

        static ClientConnections* threadInstance() {
            ClientConnections* cc = _perThread.get();
            if ( ! cc ) {
                cc = new ClientConnections();
                _perThread.reset( cc );
            }
            return cc;
        }
    };

    thread_specific_ptr<ClientConnections> ClientConnections::_perThread;

    /**
     * Appends info about all active client shard connections to a BOBuilder
     */
    void ActiveClientConnections::appendInfo( BSONObjBuilder& b ) {

        BSONArrayBuilder arr( 64 * 1024 ); // There may be quite a few threads

        {
            scoped_lock lock( _mutex );
            for ( set<const ClientConnections*>::const_iterator i = _clientConnections.begin();
                  i != _clientConnections.end(); ++i )
            {
                BSONObjBuilder bb( arr.subobjStart() );
                (*i)->appendInfo( bb );
                bb.done();
            }
        }

        b.appendArray( "threads", arr.obj() );
    }

    ShardConnection::ShardConnection( const Shard * s , const string& ns, ChunkManagerPtr manager )
        : _addr( s->getConnString() ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    ShardConnection::ShardConnection( const Shard& s , const string& ns, ChunkManagerPtr manager )
        : _addr( s.getConnString() ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    ShardConnection::ShardConnection( const string& addr , const string& ns, ChunkManagerPtr manager )
        : _addr( addr ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    void usingAShardConnection( const string& addr );

    void ShardConnection::_init() {
        verify( _addr.size() );
        _conn = ClientConnections::threadInstance()->get( _addr , _ns );
        _finishedInit = false;
        usingAShardConnection( _addr );
    }

    void ShardConnection::_finishInit() {
        if ( _finishedInit )
            return;
        _finishedInit = true;

        if ( _ns.size() && versionManager.isVersionableCB( _conn ) ) {
            // Make sure we specified a manager for the correct namespace
            if( _manager ) verify( _manager->getns() == _ns );
            _setVersion = versionManager.checkShardVersionCB( this , false , 1 );
        }
        else {
            // Make sure we didn't specify a manager for an empty namespace
            verify( ! _manager );
            _setVersion = false;
        }

    }

    void ShardConnection::done() {
        if ( _conn ) {
            ClientConnections::threadInstance()->done( _addr , _conn );
            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::kill() {
        if ( _conn ) {
            if( versionManager.isVersionableCB( _conn ) ) versionManager.resetShardVersionCB( _conn );

            if (_conn->isFailed()) {
                // Let the pool know about the bad connection and also delegate disposal to it.
                ClientConnections::threadInstance()->done(_addr, _conn);
            }
            else {
                delete _conn;
            }

            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::sync() {
        ClientConnections::threadInstance()->sync();
    }

    bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {
        verify( _conn );
        bool ok = _conn->runCommand( db , cmd , res );
        if ( ! ok ) {
            if ( res["code"].numberInt() == SendStaleConfigCode ) {
                done();
                throw RecvStaleConfigException( res["errmsg"].String(), res );
            }
        }
        return ok;
    }

    void ShardConnection::checkMyConnectionVersions( const string & ns ) {
        ClientConnections::threadInstance()->checkVersions( ns );
    }

    ShardConnection::~ShardConnection() {
        if ( _conn ) {
            if (_conn->isFailed()) {
                if (_conn->getSockCreationMicroSec() ==
                        DBClientBase::INVALID_SOCK_CREATION_TIME) {
                    kill();
                }
                else {
                    // The pool takes care of deleting the failed connection - this
                    // will also trigger disposal of older connections in the pool
                    done();
                }
            }
            else {
                /* see done() comments above for why we log this line */
                log() << "sharded connection to " << _conn->getServerAddress()
                        << " not being returned to the pool" << endl;

                kill();
            }
        }
    }

    bool ShardConnection::releaseConnectionsAfterResponse( false );

    ExportedServerParameter<bool> ReleaseConnectionsAfterResponse(
        ServerParameterSet::getGlobal(),
        "releaseConnectionsAfterResponse",
         &ShardConnection::releaseConnectionsAfterResponse,
        true,
        true
    );

    void ShardConnection::releaseMyConnections() {
        ClientConnections::threadInstance()->releaseAll();
    }

    void ShardConnection::clearPool() {
        shardConnectionPool.clear();
        ClientConnections::threadInstance()->clearPool();
    }

    void ShardConnection::forgetNS( const string& ns ) {
        ClientConnections::threadInstance()->forgetNS( ns );
    }
}
コード例 #16
0
namespace mongo {

using std::unique_ptr;
using std::map;
using std::set;
using std::string;
using std::stringstream;
using std::vector;

namespace {

class ClientConnections;

/**
 * Class which tracks ClientConnections (the client connection pool) for each incoming
 * connection, allowing stats access.
 */
class ActiveClientConnections {
public:
    void add(const ClientConnections* cc) {
        stdx::lock_guard<stdx::mutex> lock(_mutex);
        _clientConnections.insert(cc);
    }

    void remove(const ClientConnections* cc) {
        stdx::lock_guard<stdx::mutex> lock(_mutex);
        _clientConnections.erase(cc);
    }

    void appendInfo(BSONObjBuilder& b);

private:
    stdx::mutex _mutex;
    set<const ClientConnections*> _clientConnections;

} activeClientConnections;

/**
 * Command to allow access to the sharded conn pool information in mongos.
 */
class ShardedPoolStats : public Command {
public:
    ShardedPoolStats() : Command("shardConnPoolStats") {}
    virtual void help(stringstream& help) const {
        help << "stats about the shard connection pool";
    }
    virtual bool isWriteCommandForConfigServer() const {
        return false;
    }
    virtual bool slaveOk() const {
        return true;
    }

    // Same privs as connPoolStats
    virtual void addRequiredPrivileges(const std::string& dbname,
                                       const BSONObj& cmdObj,
                                       std::vector<Privilege>* out) {
        ActionSet actions;
        actions.addAction(ActionType::connPoolStats);
        out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
    }

    virtual bool run(OperationContext* txn,
                     const string& dbname,
                     mongo::BSONObj& cmdObj,
                     int options,
                     std::string& errmsg,
                     mongo::BSONObjBuilder& result) {
        // Base pool info
        shardConnectionPool.appendInfo(result);

        // Thread connection info
        activeClientConnections.appendInfo(result);

        return true;
    }

} shardedPoolStatsCmd;

/**
 * holds all the actual db connections for a client to various servers 1 per thread, so
 * doesn't have to be thread safe.
 */
class ClientConnections {
    MONGO_DISALLOW_COPYING(ClientConnections);

public:
    struct Status {
        Status() : created(0), avail(0) {}

        // May be read concurrently, but only written from
        // this thread.
        long long created;
        DBClientBase* avail;
    };

    // Gets or creates the status object for the host
    Status* _getStatus(const string& addr) {
        scoped_spinlock lock(_lock);
        Status*& temp = _hosts[addr];
        if (!temp) {
            temp = new Status();
        }

        return temp;
    }

    ClientConnections() {
        // Start tracking client connections
        activeClientConnections.add(this);
    }

    ~ClientConnections() {
        // Stop tracking these client connections
        activeClientConnections.remove(this);

        releaseAll(true);
    }

    void releaseAll(bool fromDestructor = false) {
        // Don't need spinlock protection because if not in the destructor, we don't modify
        // _hosts, and if in the destructor we are not accessible to external threads.
        for (HostMap::iterator i = _hosts.begin(); i != _hosts.end(); ++i) {
            const string addr = i->first;
            Status* ss = i->second;
            invariant(ss);

            if (ss->avail) {
                // If we're shutting down, don't want to initiate release mechanism as it is
                // slow, and isn't needed since all connections will be closed anyway.
                if (inShutdown()) {
                    if (versionManager.isVersionableCB(ss->avail)) {
                        versionManager.resetShardVersionCB(ss->avail);
                    }

                    delete ss->avail;
                } else {
                    release(addr, ss->avail);
                }

                ss->avail = 0;
            }

            if (fromDestructor) {
                delete ss;
            }
        }

        if (fromDestructor) {
            _hosts.clear();
        }
    }

    DBClientBase* get(const string& addr, const string& ns) {
        {
            // We want to report ns stats
            scoped_spinlock lock(_lock);
            if (ns.size() > 0)
                _seenNS.insert(ns);
        }

        Status* s = _getStatus(addr);

        unique_ptr<DBClientBase> c;
        if (s->avail) {
            c.reset(s->avail);
            s->avail = 0;

            // May throw an exception
            shardConnectionPool.onHandedOut(c.get());
        } else {
            c.reset(shardConnectionPool.get(addr));

            // After, so failed creation doesn't get counted
            s->created++;
        }

        return c.release();
    }

    void done(const string& addr, DBClientBase* conn) {
        Status* s = _hosts[addr];
        verify(s);

        const bool isConnGood = shardConnectionPool.isConnectionGood(addr, conn);

        if (s->avail != NULL) {
            warning() << "Detected additional sharded connection in the "
                      << "thread local pool for " << addr;

            if (DBException::traceExceptions) {
                // There shouldn't be more than one connection checked out to the same
                // host on the same thread.
                printStackTrace();
            }

            if (!isConnGood) {
                delete s->avail;
                s->avail = NULL;
            }

            // Let the internal pool handle the bad connection, this can also
            // update the lower bounds for the known good socket creation time
            // for this host.
            release(addr, conn);
            return;
        }

        if (!isConnGood) {
            // Let the internal pool handle the bad connection.
            release(addr, conn);
            return;
        }

        // Note: Although we try our best to clear bad connections as much as possible,
        // some of them can still slip through because of how ClientConnections are being
        // used - as thread local variables. This means that threads won't be able to
        // see the s->avail connection of other threads.

        s->avail = conn;
    }

    void sync() {
        for (HostMap::iterator i = _hosts.begin(); i != _hosts.end(); ++i) {
            string addr = i->first;
            Status* ss = i->second;
            if (ss->avail)
                ss->avail->getLastError();
        }
    }

    void checkVersions(OperationContext* txn, const string& ns) {
        vector<ShardId> all;
        grid.shardRegistry()->getAllShardIds(&all);

        // Don't report exceptions here as errors in GetLastError
        LastError::Disabled ignoreForGLE(&LastError::get(cc()));

        // Now only check top-level shard connections
        for (const ShardId& shardId : all) {
            try {
                const auto shard = grid.shardRegistry()->getShard(txn, shardId);
                if (!shard) {
                    continue;
                }

                string sconnString = shard->getConnString().toString();
                Status* s = _getStatus(sconnString);

                if (!s->avail) {
                    s->avail = shardConnectionPool.get(sconnString);
                    s->created++;  // After, so failed creation doesn't get counted
                }

                versionManager.checkShardVersionCB(txn, s->avail, ns, false, 1);
            } catch (const DBException& ex) {
                warning() << "problem while initially checking shard versions on"
                          << " " << shardId << causedBy(ex);

                // NOTE: This is only a heuristic, to avoid multiple stale version retries
                // across multiple shards, and does not affect correctness.
            }
        }
    }

    void release(const string& addr, DBClientBase* conn) {
        shardConnectionPool.release(addr, conn);
    }

    /**
     * Appends info about the client connection pool to a BOBuilder
     * Safe to call with activeClientConnections lock
     */
    void appendInfo(BSONObjBuilder& b) const {
        scoped_spinlock lock(_lock);

        BSONArrayBuilder hostsArrB(b.subarrayStart("hosts"));
        for (HostMap::const_iterator i = _hosts.begin(); i != _hosts.end(); ++i) {
            BSONObjBuilder bb(hostsArrB.subobjStart());
            bb.append("host", i->first);
            bb.append("created", i->second->created);
            bb.appendBool("avail", static_cast<bool>(i->second->avail));
            bb.done();
        }
        hostsArrB.done();

        BSONArrayBuilder nsArrB(b.subarrayStart("seenNS"));
        for (set<string>::const_iterator i = _seenNS.begin(); i != _seenNS.end(); ++i) {
            nsArrB.append(*i);
        }
        nsArrB.done();
    }

    // Protects only the creation of new entries in the _hosts and _seenNS map
    // from external threads.  Reading _hosts / _seenNS in this thread doesn't
    // need protection.
    mutable SpinLock _lock;
    typedef map<string, Status*, DBConnectionPool::serverNameCompare> HostMap;
    HostMap _hosts;
    set<string> _seenNS;

    /**
     * Clears the connections kept by this pool (ie, not including the global pool)
     */
    void clearPool() {
        for (HostMap::iterator iter = _hosts.begin(); iter != _hosts.end(); ++iter) {
            if (iter->second->avail != NULL) {
                delete iter->second->avail;
            }
            delete iter->second;
        }

        _hosts.clear();
    }

    void forgetNS(const string& ns) {
        scoped_spinlock lock(_lock);
        _seenNS.erase(ns);
    }

    // -----

    static thread_specific_ptr<ClientConnections> _perThread;

    static ClientConnections* threadInstance() {
        ClientConnections* cc = _perThread.get();
        if (!cc) {
            cc = new ClientConnections();
            _perThread.reset(cc);
        }
        return cc;
    }
};


void ActiveClientConnections::appendInfo(BSONObjBuilder& b) {
    BSONArrayBuilder arr(64 * 1024);  // There may be quite a few threads

    {
        stdx::lock_guard<stdx::mutex> lock(_mutex);
        for (set<const ClientConnections*>::const_iterator i = _clientConnections.begin();
             i != _clientConnections.end();
             ++i) {
            BSONObjBuilder bb(arr.subobjStart());
            (*i)->appendInfo(bb);
            bb.done();
        }
    }

    b.appendArray("threads", arr.obj());
}

thread_specific_ptr<ClientConnections> ClientConnections::_perThread;

}  // namespace

// The global connection pool
DBConnectionPool shardConnectionPool;

// Different between mongos and mongod
void usingAShardConnection(const string& addr);

ShardConnection::ShardConnection(const ConnectionString& connectionString,
                                 const string& ns,
                                 std::shared_ptr<ChunkManager> manager)
    : _cs(connectionString), _ns(ns), _manager(manager), _finishedInit(false) {
    invariant(_cs.isValid());

    // Make sure we specified a manager for the correct namespace
    if (_ns.size() && _manager) {
        invariant(_manager->getns() == _ns);
    }

    _conn = ClientConnections::threadInstance()->get(_cs.toString(), _ns);
    usingAShardConnection(_cs.toString());
}

ShardConnection::~ShardConnection() {
    if (_conn) {
        if (_conn->isFailed()) {
            if (_conn->getSockCreationMicroSec() == DBClientBase::INVALID_SOCK_CREATION_TIME) {
                kill();
            } else {
                // The pool takes care of deleting the failed connection - this
                // will also trigger disposal of older connections in the pool
                done();
            }
        } else {
            // see done() comments above for why we log this line
            log() << "sharded connection to " << _conn->getServerAddress()
                  << " not being returned to the pool";

            kill();
        }
    }
}

void ShardConnection::_finishInit() {
    if (_finishedInit)
        return;
    _finishedInit = true;

    if (versionManager.isVersionableCB(_conn)) {
        auto& client = cc();
        auto txn = client.getOperationContext();
        invariant(txn);
        _setVersion = versionManager.checkShardVersionCB(txn, this, false, 1);
    } else {
        // Make sure we didn't specify a manager for a non-versionable connection (i.e. config)
        verify(!_manager);
        _setVersion = false;
    }
}

void ShardConnection::done() {
    if (_conn) {
        ClientConnections::threadInstance()->done(_cs.toString(), _conn);
        _conn = 0;
        _finishedInit = true;
    }
}

void ShardConnection::kill() {
    if (_conn) {
        if (versionManager.isVersionableCB(_conn)) {
            versionManager.resetShardVersionCB(_conn);
        }

        if (_conn->isFailed()) {
            // Let the pool know about the bad connection and also delegate disposal to it.
            ClientConnections::threadInstance()->done(_cs.toString(), _conn);
        } else {
            delete _conn;
        }

        _conn = 0;
        _finishedInit = true;
    }
}

void ShardConnection::sync() {
    ClientConnections::threadInstance()->sync();
}

void ShardConnection::checkMyConnectionVersions(OperationContext* txn, const string& ns) {
    ClientConnections::threadInstance()->checkVersions(txn, ns);
}

void ShardConnection::releaseMyConnections() {
    ClientConnections::threadInstance()->releaseAll();
}

void ShardConnection::clearPool() {
    shardConnectionPool.clear();
    ClientConnections::threadInstance()->clearPool();
}

void ShardConnection::forgetNS(const string& ns) {
    ClientConnections::threadInstance()->forgetNS(ns);
}

}  // namespace mongo
コード例 #17
0
ファイル: connpool.cpp プロジェクト: tanfulai/mongo
namespace mongo {

    DBConnectionPool pool;
    
    DBClientBase* DBConnectionPool::get(const string& host) {
        boostlock L(poolMutex);

        PoolForHost *&p = pools[host];
        if ( p == 0 )
            p = new PoolForHost();
        if ( p->pool.empty() ) {
            string errmsg;
            DBClientBase *c;
            if( host.find(',') == string::npos ) {
                DBClientConnection *cc = new DBClientConnection(true);
                if ( !cc->connect(host.c_str(), errmsg) ) {
                    delete cc;
                    uassert( (string)"dbconnectionpool: connect failed" + host , false);
                    return 0;
                }
                c = cc;
            }
            else { 
                DBClientPaired *p = new DBClientPaired();
                if( !p->connect(host) ) { 
                    delete p;
                    uassert( (string)"dbconnectionpool: connect failed [2] " + host , false);
                    return 0;
                }
                c = p;
            }
            return c;
        }
        DBClientBase *c = p->pool.front();
        p->pool.pop();
        return c;
    }

    void DBConnectionPool::flush(){
        boostlock L(poolMutex);
        for ( map<string,PoolForHost*>::iterator i = pools.begin(); i != pools.end(); i++ ){
            PoolForHost* p = i->second;

            vector<DBClientBase*> all;
            while ( ! p->pool.empty() ){
                DBClientBase * c = p->pool.front();
                p->pool.pop();
                all.push_back( c );
                bool res;
                c->isMaster( res );
            }
            
            for ( vector<DBClientBase*>::iterator i=all.begin(); i != all.end(); i++ ){
                p->pool.push( *i );
            }
        }
    }

    class PoolFlushCmd : public Command {
    public:
        PoolFlushCmd() : Command( "connpoolsync" ){}
        virtual bool run(const char*, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool){
            pool.flush();
            result << "ok" << 1;
            return true;
        }
        virtual bool slaveOk(){
            return true;
        }

    } poolFlushCmd;

} // namespace mongo
コード例 #18
0
ファイル: connpool.cpp プロジェクト: gregstuder/mongo
namespace mongo {

    // ------ PoolForHost ------

    PoolForHost::~PoolForHost() {
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            delete sc.conn;
            _pool.pop();
        }
    }

    void PoolForHost::done( DBConnectionPool * pool, DBClientBase * c ) {
        if ( _pool.size() >= _maxPerHost ) {
            pool->onDestroy( c );
            delete c;
        }
        else {
            _pool.push(c);
        }
    }

    DBClientBase * PoolForHost::get( DBConnectionPool * pool , double socketTimeout ) {

        time_t now = time(0);
        
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            _pool.pop();
            
            if ( ! sc.ok( now ) )  {
                pool->onDestroy( sc.conn );
                delete sc.conn;
                continue;
            }
            
            assert( sc.conn->getSoTimeout() == socketTimeout );

            return sc.conn;

        }

        return NULL;
    }

    void PoolForHost::flush() {
        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            all.push_back( c );
            bool res;
            c.conn->isMaster( res );
        }

        for ( vector<StoredConnection>::iterator i=all.begin(); i != all.end(); ++i ) {
            _pool.push( *i );
        }
    }

    void PoolForHost::getStaleConnections( vector<DBClientBase*>& stale ) {
        time_t now = time(0);

        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            
            if ( c.ok( now ) )
                all.push_back( c );
            else
                stale.push_back( c.conn );
        }

        for ( size_t i=0; i<all.size(); i++ ) {
            _pool.push( all[i] );
        }
    }


    PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ) {
        conn = c;
        when = time(0);
    }

    bool PoolForHost::StoredConnection::ok( time_t now ) {
        // if connection has been idle for 30 minutes, kill it
        return ( now - when ) < 1800;
    }

    void PoolForHost::createdOne( DBClientBase * base) {
        if ( _created == 0 )
            _type = base->type();
        _created++;
    }

    unsigned PoolForHost::_maxPerHost = 50;

    // ------ DBConnectionPool ------

    DBConnectionPool pool;

    DBConnectionPool::DBConnectionPool() 
        : _mutex("DBConnectionPool") , 
          _name( "dbconnectionpool" ) , 
          _hooks( new list<DBConnectionHook*>() ) { 
    }

    DBClientBase* DBConnectionPool::_get(const string& ident , double socketTimeout ) {
        assert( ! inShutdown() );
        scoped_lock L(_mutex);
        PoolForHost& p = _pools[PoolKey(ident,socketTimeout)];
        return p.get( this , socketTimeout );
    }

    DBClientBase* DBConnectionPool::_finishCreate( const string& host , double socketTimeout , DBClientBase* conn ) {
        {
            scoped_lock L(_mutex);
            PoolForHost& p = _pools[PoolKey(host,socketTimeout)];
            p.createdOne( conn );
        }
        
        try {
            onCreate( conn );
            onHandedOut( conn );
        }
        catch ( std::exception& e ) {
            delete conn;
            throw;
        }

        return conn;
    }

    DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
        DBClientBase * c = _get( url.toString() , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& e ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        c = url.connect( errmsg, socketTimeout );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );

        return _finishCreate( url.toString() , socketTimeout , c );
    }

    DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
        DBClientBase * c = _get( host , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& e ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        ConnectionString cs = ConnectionString::parse( host , errmsg );
        uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );

        c = cs.connect( errmsg, socketTimeout );
        if ( ! c )
            throw SocketException( SocketException::CONNECT_ERROR , host , 11002 , str::stream() << _name << " error: " << errmsg );
        return _finishCreate( host , socketTimeout , c );
    }

    void DBConnectionPool::release(const string& host, DBClientBase *c) {
        if ( c->isFailed() ) {
            onDestroy( c );
            delete c;
            return;
        }
        scoped_lock L(_mutex);
        _pools[PoolKey(host,c->getSoTimeout())].done(this,c);
    }


    DBConnectionPool::~DBConnectionPool() {
        // connection closing is handled by ~PoolForHost
    }

    void DBConnectionPool::flush() {
        scoped_lock L(_mutex);
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++ ) {
            PoolForHost& p = i->second;
            p.flush();
        }
    }

    void DBConnectionPool::addHook( DBConnectionHook * hook ) {
        _hooks->push_back( hook );
    }

    void DBConnectionPool::onCreate( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onCreate( conn );
        }
    }

    void DBConnectionPool::onHandedOut( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onHandedOut( conn );
        }
    }

    void DBConnectionPool::onDestroy( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onDestroy( conn );
        }
    }

    void DBConnectionPool::appendInfo( BSONObjBuilder& b ) {

        int avail = 0;
        long long created = 0;


        map<ConnectionString::ConnectionType,long long> createdByType;

        set<string> replicaSets;
        
        BSONObjBuilder bb( b.subobjStart( "hosts" ) );
        {
            scoped_lock lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                if ( i->second.numCreated() == 0 )
                    continue;

                string s = str::stream() << i->first.ident << "::" << i->first.timeout;

                BSONObjBuilder temp( bb.subobjStart( s ) );
                temp.append( "available" , i->second.numAvailable() );
                temp.appendNumber( "created" , i->second.numCreated() );
                temp.done();

                avail += i->second.numAvailable();
                created += i->second.numCreated();

                long long& x = createdByType[i->second.type()];
                x += i->second.numCreated();

                {
                    string setName = i->first.ident;
                    if ( setName.find( "/" ) != string::npos ) {
                        setName = setName.substr( 0 , setName.find( "/" ) );
                        replicaSets.insert( setName );
                    }
                }
            }
        }
        bb.done();
        
        
        BSONObjBuilder setBuilder( b.subobjStart( "replicaSets" ) );
        for ( set<string>::iterator i=replicaSets.begin(); i!=replicaSets.end(); ++i ) {
            string rs = *i;
            ReplicaSetMonitorPtr m = ReplicaSetMonitor::get( rs );
            if ( ! m ) {
                warning() << "no monitor for set: " << rs << endl;
                continue;
            }
            
            BSONObjBuilder temp( setBuilder.subobjStart( rs ) );
            m->appendInfo( temp );
            temp.done();
        }
        setBuilder.done();

        {
            BSONObjBuilder temp( bb.subobjStart( "createdByType" ) );
            for ( map<ConnectionString::ConnectionType,long long>::iterator i=createdByType.begin(); i!=createdByType.end(); ++i ) {
                temp.appendNumber( ConnectionString::typeToString( i->first ) , i->second );
            }
            temp.done();
        }

        b.append( "totalAvailable" , avail );
        b.appendNumber( "totalCreated" , created );
    }

    bool DBConnectionPool::serverNameCompare::operator()( const string& a , const string& b ) const{
        const char* ap = a.c_str();
        const char* bp = b.c_str();
       
        while (true){
            if (*ap == '\0' || *ap == '/'){
                if (*bp == '\0' || *bp == '/')
                    return false; // equal strings
                else
                    return true; // a is shorter
            }

            if (*bp == '\0' || *bp == '/')
                return false; // b is shorter
            
            if ( *ap < *bp)
                return true;
            else if (*ap > *bp)
                return false;

            ++ap;
            ++bp;
        }
        assert(false);
    }
    
    bool DBConnectionPool::poolKeyCompare::operator()( const PoolKey& a , const PoolKey& b ) const {
        if (DBConnectionPool::serverNameCompare()( a.ident , b.ident ))
            return true;
        
        if (DBConnectionPool::serverNameCompare()( b.ident , a.ident ))
            return false;

        return a.timeout < b.timeout;
    }


    void DBConnectionPool::taskDoWork() { 
        vector<DBClientBase*> toDelete;
        
        {
            // we need to get the connections inside the lock
            // but we can actually delete them outside
            scoped_lock lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                i->second.getStaleConnections( toDelete );
            }
        }

        for ( size_t i=0; i<toDelete.size(); i++ ) {
            try {
                onDestroy( toDelete[i] );
                delete toDelete[i];
            }
            catch ( ... ) {
                // we don't care if there was a socket error
            }
        }
    }

    // ------ ScopedDbConnection ------

    ScopedDbConnection * ScopedDbConnection::steal() {
        assert( _conn );
        ScopedDbConnection * n = new ScopedDbConnection( _host , _conn, _socketTimeout );
        _conn = 0;
        return n;
    }

    void ScopedDbConnection::_setSocketTimeout(){
        if( ! _conn ) return;
        if( _conn->type() == ConnectionString::MASTER )
            (( DBClientConnection* ) _conn)->setSoTimeout( _socketTimeout );
        else if( _conn->type() == ConnectionString::SYNC )
            (( SyncClusterConnection* ) _conn)->setAllSoTimeouts( _socketTimeout );
    }

    ScopedDbConnection::~ScopedDbConnection() {
        if ( _conn ) {
            if ( ! _conn->isFailed() ) {
                /* see done() comments above for why we log this line */
                log() << "~ScopedDbConnection: _conn != null" << endl;
            }
            kill();
        }
    }

    ScopedDbConnection::ScopedDbConnection(const Shard& shard, double socketTimeout )
        : _host( shard.getConnString() ) , _conn( pool.get(_host, socketTimeout) ), _socketTimeout( socketTimeout ) {
        _setSocketTimeout();
    }

    ScopedDbConnection::ScopedDbConnection(const Shard* shard, double socketTimeout )
        : _host( shard->getConnString() ) , _conn( pool.get(_host, socketTimeout) ), _socketTimeout( socketTimeout ) {
        _setSocketTimeout();
    }

    AtomicUInt AScopedConnection::_numConnections;

} // namespace mongo
コード例 #19
0
ファイル: connpool.cpp プロジェクト: gregstuder/mongo
 ScopedDbConnection::ScopedDbConnection(const Shard* shard, double socketTimeout )
     : _host( shard->getConnString() ) , _conn( pool.get(_host, socketTimeout) ), _socketTimeout( socketTimeout ) {
     _setSocketTimeout();
 }
コード例 #20
0
ファイル: connpool.cpp プロジェクト: chrelad/mongo
 ScopedDbConnection::ScopedDbConnection(const Shard* shard )
     : _host( shard->getConnString() ) , _conn( pool.get(_host) ){
 }
コード例 #21
0
ファイル: connpool.cpp プロジェクト: elenno/MyServer
 virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool) {
     pool.appendInfo( result );
     result.append( "numDBClientConnection" , DBClientConnection::getNumConnections() );
     result.append( "numAScopedConnection" , AScopedConnection::getNumConnections() );
     return true;
 }
コード例 #22
0
ファイル: connpool.cpp プロジェクト: chrelad/mongo
 virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool){
     pool.appendInfo( result );
     return true;
 }
コード例 #23
0
ファイル: connpool.cpp プロジェクト: chrelad/mongo
namespace mongo {

    // ------ PoolForHost ------
    
    PoolForHost::~PoolForHost(){
        while ( ! _pool.empty() ){
            StoredConnection sc = _pool.top();
            delete sc.conn;
            _pool.pop();
        }
    }

    void PoolForHost::done( DBClientBase * c ) {
        _pool.push(c);
    }
    
    DBClientBase * PoolForHost::get() {
        
        time_t now = time(0);
        
        while ( ! _pool.empty() ){
            StoredConnection sc = _pool.top();
            _pool.pop();
            if ( sc.ok( now ) )
                return sc.conn;
            delete sc.conn;
        }
        
        return NULL;
    }
    
    void PoolForHost::flush() {
        vector<StoredConnection> all;
        while ( ! _pool.empty() ){
            StoredConnection c = _pool.top();
            _pool.pop();
            all.push_back( c );
            bool res;
            c.conn->isMaster( res );
        }
        
        for ( vector<StoredConnection>::iterator i=all.begin(); i != all.end(); ++i ){
            _pool.push( *i );
        }
    }

    PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ){
        conn = c;
        when = time(0);
    }

    bool PoolForHost::StoredConnection::ok( time_t now ){
        // if connection has been idle for an hour, kill it
        return ( now - when ) < 3600;
    }
    // ------ DBConnectionPool ------

    DBConnectionPool pool;
    
    DBClientBase* DBConnectionPool::_get(const string& ident) {
        scoped_lock L(_mutex);
        PoolForHost& p = _pools[ident];
        return p.get();
    }

    DBClientBase* DBConnectionPool::_finishCreate( const string& host , DBClientBase* conn ){
        {
            scoped_lock L(_mutex);
            PoolForHost& p = _pools[host];
            p.createdOne();
        }

        onCreate( conn );
        onHandedOut( conn );
        
        return conn;
    }

    DBClientBase* DBConnectionPool::get(const ConnectionString& url) {
        DBClientBase * c = _get( url.toString() );
        if ( c ){
            onHandedOut( c );
            return c;
        }
        
        string errmsg;
        c = url.connect( errmsg );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );
        
        return _finishCreate( url.toString() , c );
    }
    
    DBClientBase* DBConnectionPool::get(const string& host) {
        DBClientBase * c = _get( host );
        if ( c ){
            onHandedOut( c );
            return c;
        }
        
        string errmsg;
        ConnectionString cs = ConnectionString::parse( host , errmsg );
        uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );
        
        c = cs.connect( errmsg );
        uassert( 11002 ,  _name + ": connect failed " + host + " : " + errmsg , c );
        return _finishCreate( host , c );
    }

    DBConnectionPool::~DBConnectionPool(){
        // connection closing is handled by ~PoolForHost
    }

    void DBConnectionPool::flush(){
        scoped_lock L(_mutex);
        for ( map<string,PoolForHost>::iterator i = _pools.begin(); i != _pools.end(); i++ ){
            PoolForHost& p = i->second;
            p.flush();
        }
    }

    void DBConnectionPool::addHook( DBConnectionHook * hook ){
        _hooks.push_back( hook );
    }

    void DBConnectionPool::onCreate( DBClientBase * conn ){
        if ( _hooks.size() == 0 )
            return;
        
        for ( list<DBConnectionHook*>::iterator i = _hooks.begin(); i != _hooks.end(); i++ ){
            (*i)->onCreate( conn );
        }
    }

    void DBConnectionPool::onHandedOut( DBClientBase * conn ){
        if ( _hooks.size() == 0 )
            return;
        
        for ( list<DBConnectionHook*>::iterator i = _hooks.begin(); i != _hooks.end(); i++ ){
            (*i)->onHandedOut( conn );
        }
    }

    void DBConnectionPool::appendInfo( BSONObjBuilder& b ){
        scoped_lock lk( _mutex );
        BSONObjBuilder bb( b.subobjStart( "hosts" ) );
        for ( map<string,PoolForHost>::iterator i=_pools.begin(); i!=_pools.end(); ++i ){
            string s = i->first;
            BSONObjBuilder temp( bb.subobjStart( s ) );
            temp.append( "available" , i->second.numAvailable() );
            temp.appendNumber( "created" , i->second.numCreated() );
            temp.done();
        }
        bb.done();
    }

    ScopedDbConnection * ScopedDbConnection::steal(){
        assert( _conn );
        ScopedDbConnection * n = new ScopedDbConnection( _host , _conn );
        _conn = 0;
        return n;
    }
    
    ScopedDbConnection::~ScopedDbConnection() {
        if ( _conn ){
            if ( ! _conn->isFailed() ) {
                /* see done() comments above for why we log this line */
                log() << "~ScopedDbConnection: _conn != null" << endl;
            }
            kill();
        }
    }

    ScopedDbConnection::ScopedDbConnection(const Shard& shard )
        : _host( shard.getConnString() ) , _conn( pool.get(_host) ){
    }
    
    ScopedDbConnection::ScopedDbConnection(const Shard* shard )
        : _host( shard->getConnString() ) , _conn( pool.get(_host) ){
    }


    class PoolFlushCmd : public Command {
    public:
        PoolFlushCmd() : Command( "connPoolSync" , false , "connpoolsync" ){}
        virtual void help( stringstream &help ) const { help<<"internal"; }
        virtual LockType locktype() const { return NONE; }
        virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool){
            pool.flush();
            return true;
        }
        virtual bool slaveOk() const {
            return true;
        }

    } poolFlushCmd;

    class PoolStats : public Command {
    public:
        PoolStats() : Command( "connPoolStats" ){}
        virtual void help( stringstream &help ) const { help<<"stats about connection pool"; }
        virtual LockType locktype() const { return NONE; }
        virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool){
            pool.appendInfo( result );
            return true;
        }
        virtual bool slaveOk() const {
            return true;
        }

    } poolStatsCmd;


} // namespace mongo
コード例 #24
0
ファイル: connpool.cpp プロジェクト: tanfulai/mongo
 virtual bool run(const char*, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool){
     pool.flush();
     result << "ok" << 1;
     return true;
 }
コード例 #25
0
namespace mongo {

    DBConnectionPool shardConnectionPool;

    /**
     * holds all the actual db connections for a client to various servers
     * 1 per thread, so doesn't have to be thread safe
     */
    class ClientConnections : boost::noncopyable {
    public:
        struct Status : boost::noncopyable {
            Status() : created(0), avail(0) {}

            long long created;
            DBClientBase* avail;
        };


        ClientConnections() {}

        ~ClientConnections() {
            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                assert( ss );
                if ( ss->avail ) {
                    /* if we're shutting down, don't want to initiate release mechanism as it is slow,
                       and isn't needed since all connections will be closed anyway */
                    if ( inShutdown() ) {
                        if( versionManager.isVersionableCB( ss->avail ) ) versionManager.resetShardVersionCB( ss->avail );
                        delete ss->avail;
                    }
                    else
                        release( addr , ss->avail );
                    ss->avail = 0;
                }
                delete ss;
            }
            _hosts.clear();
        }

        DBClientBase * get( const string& addr , const string& ns ) {
            _check( ns );

            Status* &s = _hosts[addr];
            if ( ! s )
                s = new Status();

            if ( s->avail ) {
                DBClientBase* c = s->avail;
                s->avail = 0;
                try {
                    shardConnectionPool.onHandedOut( c );
                }
                catch ( std::exception& e ) {
                    delete c;
                    throw;
                }
                return c;
            }

            s->created++;
            return shardConnectionPool.get( addr );
        }

        void done( const string& addr , DBClientBase* conn ) {
            Status* s = _hosts[addr];
            assert( s );
            if ( s->avail ) {
                release( addr , conn );
                return;
            }
            s->avail = conn;
        }

        void sync() {
            for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
                string addr = i->first;
                Status* ss = i->second;
                if ( ss->avail )
                    ss->avail->getLastError();
                
            }
        }

        void checkVersions( const string& ns ) {

            vector<Shard> all;
            Shard::getAllShards( all );

            // Now only check top-level shard connections
            for ( unsigned i=0; i<all.size(); i++ ) {

                string sconnString = all[i].getConnString();
                Status* &s = _hosts[sconnString];

                if ( ! s ){
                    s = new Status();
                }

                if( ! s->avail )
                    s->avail = shardConnectionPool.get( sconnString );

                versionManager.checkShardVersionCB( s->avail, ns, false, 1 );

            }
        }

        void release( const string& addr , DBClientBase * conn ) {
            shardConnectionPool.release( addr , conn );
        }

        void _check( const string& ns ) {
            if ( ns.size() == 0 || _seenNS.count( ns ) )
                return;
            _seenNS.insert( ns );
            checkVersions( ns );
        }
        
        typedef map<string,Status*,DBConnectionPool::serverNameCompare> HostMap;
        HostMap _hosts;
        set<string> _seenNS;
        // -----

        static thread_specific_ptr<ClientConnections> _perThread;

        static ClientConnections* threadInstance() {
            ClientConnections* cc = _perThread.get();
            if ( ! cc ) {
                cc = new ClientConnections();
                _perThread.reset( cc );
            }
            return cc;
        }
    };

    thread_specific_ptr<ClientConnections> ClientConnections::_perThread;

    ShardConnection::ShardConnection( const Shard * s , const string& ns, ChunkManagerPtr manager )
        : _addr( s->getConnString() ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    ShardConnection::ShardConnection( const Shard& s , const string& ns, ChunkManagerPtr manager )
        : _addr( s.getConnString() ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    ShardConnection::ShardConnection( const string& addr , const string& ns, ChunkManagerPtr manager )
        : _addr( addr ) , _ns( ns ), _manager( manager ) {
        _init();
    }

    void ShardConnection::_init() {
        assert( _addr.size() );
        _conn = ClientConnections::threadInstance()->get( _addr , _ns );
        _finishedInit = false;
    }

    void ShardConnection::_finishInit() {
        if ( _finishedInit )
            return;
        _finishedInit = true;

        if ( _ns.size() && versionManager.isVersionableCB( _conn ) ) {
            // Make sure we specified a manager for the correct namespace
            if( _manager ) assert( _manager->getns() == _ns );
            _setVersion = versionManager.checkShardVersionCB( this , false , 1 );
        }
        else {
            // Make sure we didn't specify a manager for an empty namespace
            assert( ! _manager );
            _setVersion = false;
        }

    }

    void ShardConnection::done() {
        if ( _conn ) {
            ClientConnections::threadInstance()->done( _addr , _conn );
            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::kill() {
        if ( _conn ) {
            if( versionManager.isVersionableCB( _conn ) ) versionManager.resetShardVersionCB( _conn );
            delete _conn;
            _conn = 0;
            _finishedInit = true;
        }
    }

    void ShardConnection::sync() {
        ClientConnections::threadInstance()->sync();
    }

    bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {
        assert( _conn );
        bool ok = _conn->runCommand( db , cmd , res );
        if ( ! ok ) {
            if ( res["code"].numberInt() == SendStaleConfigCode ) {
                done();
                throw RecvStaleConfigException( res["ns"].String() , res["errmsg"].String() );
            }
        }
        return ok;
    }

    void ShardConnection::checkMyConnectionVersions( const string & ns ) {
        ClientConnections::threadInstance()->checkVersions( ns );
    }

    ShardConnection::~ShardConnection() {
        if ( _conn ) {
            if ( ! _conn->isFailed() ) {
                /* see done() comments above for why we log this line */
                log() << "~ScopedDBConnection: _conn != null" << endl;
            }
            kill();
        }
    }
}
コード例 #26
0
ファイル: connpool.cpp プロジェクト: AllenDou/mongo
namespace mongo {

    // ------ PoolForHost ------

    PoolForHost::~PoolForHost() {
        clear();
    }

    void PoolForHost::clear() {
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            delete sc.conn;
            _pool.pop();
        }
    }

    void PoolForHost::done( DBConnectionPool * pool, DBClientBase * c ) {
        if (c->isFailed()) {
            reportBadConnectionAt(c->getSockCreationMicroSec());
            pool->onDestroy(c);
            delete c;
        }
        else if (_pool.size() >= _maxPerHost ||
                c->getSockCreationMicroSec() < _minValidCreationTimeMicroSec) {
            pool->onDestroy(c);
            delete c;
        }
        else {
            _pool.push(c);
        }
    }

    void PoolForHost::reportBadConnectionAt(uint64_t microSec) {
        if (microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
                microSec > _minValidCreationTimeMicroSec) {
            log() << "Detecting bad connection created at " << _minValidCreationTimeMicroSec
                    << " microSec, clearing pool for " << _hostName << endl;
            _minValidCreationTimeMicroSec = microSec;
            clear();
        }
    }

    bool PoolForHost::isBadSocketCreationTime(uint64_t microSec) {
        return microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
                microSec <= _minValidCreationTimeMicroSec;
    }

    DBClientBase * PoolForHost::get( DBConnectionPool * pool , double socketTimeout ) {

        time_t now = time(0);
        
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            _pool.pop();
            
            if ( ! sc.ok( now ) )  {
                pool->onDestroy( sc.conn );
                delete sc.conn;
                continue;
            }
            
            verify( sc.conn->getSoTimeout() == socketTimeout );

            return sc.conn;

        }

        return NULL;
    }

    void PoolForHost::flush() {
        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            bool res;
            bool alive = false;
            // When a connection is in the pool it doesn't have an AuthenticationTable set.
            // Set the table temporarily for the isMaster command.
            c.conn->setAuthenticationTable(
                    AuthenticationTable::getInternalSecurityAuthenticationTable() );
            try {
                c.conn->isMaster( res );
                alive = true;
            } catch ( const DBException e ) {
                // There's something wrong with this connection, swallow the exception and do not
                // put the connection back in the pool.
                LOG(1) << "Exception thrown when checking pooled connection to " <<
                    c.conn->getServerAddress() << ": " << causedBy(e) << endl;
                delete c.conn;
                c.conn = NULL;
            }
            if ( alive ) {
                c.conn->clearAuthenticationTable();
                all.push_back( c );
            }
        }

        for ( vector<StoredConnection>::iterator i=all.begin(); i != all.end(); ++i ) {
            _pool.push( *i );
        }
    }

    void PoolForHost::getStaleConnections( vector<DBClientBase*>& stale ) {
        time_t now = time(0);

        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            
            if ( c.ok( now ) )
                all.push_back( c );
            else
                stale.push_back( c.conn );
        }

        for ( size_t i=0; i<all.size(); i++ ) {
            _pool.push( all[i] );
        }
    }


    PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ) {
        conn = c;
        when = time(0);
    }

    bool PoolForHost::StoredConnection::ok( time_t now ) {
        // if connection has been idle for 30 minutes, kill it
        return ( now - when ) < 1800;
    }

    void PoolForHost::createdOne( DBClientBase * base) {
        if ( _created == 0 )
            _type = base->type();
        _created++;
    }

    void PoolForHost::initializeHostName(const std::string& hostName) {
        if (_hostName.empty()) {
            _hostName = hostName;
        }
    }

    unsigned PoolForHost::_maxPerHost = 50;

    // ------ DBConnectionPool ------

    DBConnectionPool pool;

    DBConnectionPool::DBConnectionPool() 
        : _mutex("DBConnectionPool") , 
          _name( "dbconnectionpool" ) , 
          _hooks( new list<DBConnectionHook*>() ) { 
    }

    DBClientBase* DBConnectionPool::_get(const string& ident , double socketTimeout ) {
        verify( ! inShutdown() );
        scoped_lock L(_mutex);
        PoolForHost& p = _pools[PoolKey(ident,socketTimeout)];
        p.initializeHostName(ident);
        return p.get( this , socketTimeout );
    }

    DBClientBase* DBConnectionPool::_finishCreate( const string& host , double socketTimeout , DBClientBase* conn ) {
        {
            scoped_lock L(_mutex);
            PoolForHost& p = _pools[PoolKey(host,socketTimeout)];
            p.initializeHostName(host);
            p.createdOne( conn );
        }
        
        try {
            onCreate( conn );
            onHandedOut( conn );
        }
        catch ( std::exception & ) {
            delete conn;
            throw;
        }

        return conn;
    }

    DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
        DBClientBase * c = _get( url.toString() , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        c = url.connect( errmsg, socketTimeout );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );

        return _finishCreate( url.toString() , socketTimeout , c );
    }

    DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
        DBClientBase * c = _get( host , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        ConnectionString cs = ConnectionString::parse( host , errmsg );
        uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );

        c = cs.connect( errmsg, socketTimeout );
        if ( ! c )
            throw SocketException( SocketException::CONNECT_ERROR , host , 11002 , str::stream() << _name << " error: " << errmsg );
        return _finishCreate( host , socketTimeout , c );
    }

    void DBConnectionPool::release(const string& host, DBClientBase *c) {
        scoped_lock L(_mutex);
        _pools[PoolKey(host,c->getSoTimeout())].done(this,c);
    }


    DBConnectionPool::~DBConnectionPool() {
        // connection closing is handled by ~PoolForHost
    }

    void DBConnectionPool::flush() {
        scoped_lock L(_mutex);
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++ ) {
            PoolForHost& p = i->second;
            p.flush();
        }
    }

    void DBConnectionPool::clear() {
        scoped_lock L(_mutex);
        LOG(2) << "Removing connections on all pools owned by " << _name  << endl;
        for (PoolMap::iterator iter = _pools.begin(); iter != _pools.end(); ++iter) {
            iter->second.clear();
        }
    }

    void DBConnectionPool::removeHost( const string& host ) {
        scoped_lock L(_mutex);
        LOG(2) << "Removing connections from all pools for host: " << host << endl;
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); ++i ) {
            const string& poolHost = i->first.ident;
            if ( !serverNameCompare()(host, poolHost) && !serverNameCompare()(poolHost, host) ) {
                // hosts are the same
                i->second.clear();
            }
        }
    }

    void DBConnectionPool::addHook( DBConnectionHook * hook ) {
        _hooks->push_back( hook );
    }

    void DBConnectionPool::onCreate( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onCreate( conn );
        }
    }

    void DBConnectionPool::onHandedOut( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onHandedOut( conn );
        }
    }

    void DBConnectionPool::onDestroy( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onDestroy( conn );
        }
    }

    void DBConnectionPool::appendInfo( BSONObjBuilder& b ) {

        int avail = 0;
        long long created = 0;


        map<ConnectionString::ConnectionType,long long> createdByType;

        set<string> replicaSets;
        
        BSONObjBuilder bb( b.subobjStart( "hosts" ) );
        {
            scoped_lock lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                if ( i->second.numCreated() == 0 )
                    continue;

                string s = str::stream() << i->first.ident << "::" << i->first.timeout;

                BSONObjBuilder temp( bb.subobjStart( s ) );
                temp.append( "available" , i->second.numAvailable() );
                temp.appendNumber( "created" , i->second.numCreated() );
                temp.done();

                avail += i->second.numAvailable();
                created += i->second.numCreated();

                long long& x = createdByType[i->second.type()];
                x += i->second.numCreated();

                {
                    string setName = i->first.ident;
                    if ( setName.find( "/" ) != string::npos ) {
                        setName = setName.substr( 0 , setName.find( "/" ) );
                        replicaSets.insert( setName );
                    }
                }
            }
        }
        bb.done();
        
        
        BSONObjBuilder setBuilder( b.subobjStart( "replicaSets" ) );
        for ( set<string>::iterator i=replicaSets.begin(); i!=replicaSets.end(); ++i ) {
            string rs = *i;
            ReplicaSetMonitorPtr m = ReplicaSetMonitor::get( rs );
            if ( ! m ) {
                warning() << "no monitor for set: " << rs << endl;
                continue;
            }
            
            BSONObjBuilder temp( setBuilder.subobjStart( rs ) );
            m->appendInfo( temp );
            temp.done();
        }
        setBuilder.done();

        {
            BSONObjBuilder temp( bb.subobjStart( "createdByType" ) );
            for ( map<ConnectionString::ConnectionType,long long>::iterator i=createdByType.begin(); i!=createdByType.end(); ++i ) {
                temp.appendNumber( ConnectionString::typeToString( i->first ) , i->second );
            }
            temp.done();
        }

        b.append( "totalAvailable" , avail );
        b.appendNumber( "totalCreated" , created );
    }

    bool DBConnectionPool::serverNameCompare::operator()( const string& a , const string& b ) const{
        const char* ap = a.c_str();
        const char* bp = b.c_str();
       
        while (true){
            if (*ap == '\0' || *ap == '/'){
                if (*bp == '\0' || *bp == '/')
                    return false; // equal strings
                else
                    return true; // a is shorter
            }

            if (*bp == '\0' || *bp == '/')
                return false; // b is shorter
            
            if ( *ap < *bp)
                return true;
            else if (*ap > *bp)
                return false;

            ++ap;
            ++bp;
        }
        verify(false);
    }
    
    bool DBConnectionPool::poolKeyCompare::operator()( const PoolKey& a , const PoolKey& b ) const {
        if (DBConnectionPool::serverNameCompare()( a.ident , b.ident ))
            return true;
        
        if (DBConnectionPool::serverNameCompare()( b.ident , a.ident ))
            return false;

        return a.timeout < b.timeout;
    }

    bool DBConnectionPool::isConnectionGood(const string& hostName, DBClientBase* conn) {
        if (conn == NULL) {
            return false;
        }

        if (conn->isFailed()) {
            return false;
        }

        {
            scoped_lock sl(_mutex);
            PoolForHost& pool = _pools[PoolKey(hostName, conn->getSoTimeout())];
            if (pool.isBadSocketCreationTime(conn->getSockCreationMicroSec())) {
                return false;
            }
        }

        return true;
    }

    void DBConnectionPool::taskDoWork() { 
        vector<DBClientBase*> toDelete;
        
        {
            // we need to get the connections inside the lock
            // but we can actually delete them outside
            scoped_lock lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                i->second.getStaleConnections( toDelete );
            }
        }

        for ( size_t i=0; i<toDelete.size(); i++ ) {
            try {
                onDestroy( toDelete[i] );
                delete toDelete[i];
            }
            catch ( ... ) {
                // we don't care if there was a socket error
            }
        }
    }

    // ------ ScopedDbConnection ------

    void ScopedDbConnection::_setSocketTimeout(){
        if( ! _conn ) return;
        if( _conn->type() == ConnectionString::MASTER )
            (( DBClientConnection* ) _conn)->setSoTimeout( _socketTimeout );
        else if( _conn->type() == ConnectionString::SYNC )
            (( SyncClusterConnection* ) _conn)->setAllSoTimeouts( _socketTimeout );
    }

    ScopedDbConnection::~ScopedDbConnection() {
        if ( _conn ) {
            if (_conn->isFailed()) {
                if (_conn->getSockCreationMicroSec() ==
                        DBClientBase::INVALID_SOCK_CREATION_TIME) {
                    kill();
                }
                else {
                    // The pool takes care of deleting the failed connection - this
                    // will also trigger disposal of older connections in the pool
                    done();
                }
            }
            else {
                /* see done() comments above for why we log this line */
                log() << "scoped connection to " << _conn->getServerAddress()
                        << " not being returned to the pool" << endl;
                kill();
            }
        }
    }

    void ScopedDbConnection::clearPool() {
        pool.clear();
    }

    AtomicUInt AScopedConnection::_numConnections;

} // namespace mongo
コード例 #27
0
namespace mongo {
namespace {

class ClientConnections;

/**
 * Class which tracks ClientConnections (the client connection pool) for each incoming connection,
 * allowing stats access.
 */
class ActiveClientConnections {
public:
    void add(const ClientConnections* cc) {
        stdx::lock_guard<stdx::mutex> lg(_mutex);
        _clientConnections.insert(cc);
    }

    void remove(const ClientConnections* cc) {
        stdx::lock_guard<stdx::mutex> lg(_mutex);
        _clientConnections.erase(cc);
    }

    void appendInfo(BSONObjBuilder* b) const;

private:
    mutable stdx::mutex _mutex;
    std::set<const ClientConnections*> _clientConnections;

} activeClientConnections;

/**
 * Holds all the actual db connections for a client to various servers 1 per thread, so doesn't have
 * to be thread safe.
 */
class ClientConnections {
    MONGO_DISALLOW_COPYING(ClientConnections);

public:
    struct Status {
        // May be read concurrently, but only written from this thread
        long long created = 0;
        DBClientBase* avail = nullptr;
    };

    ClientConnections() {
        // Start tracking client connections
        activeClientConnections.add(this);
    }

    ~ClientConnections() {
        // Stop tracking these client connections
        activeClientConnections.remove(this);

        releaseAll(true);
    }

    static ClientConnections* threadInstance() {
        if (!_perThread) {
            _perThread = stdx::make_unique<ClientConnections>();
        }
        return _perThread.get();
    }

    DBClientBase* get(const std::string& addr, const std::string& ns) {
        {
            // We want to report ns stats
            scoped_spinlock lock(_lock);
            if (ns.size() > 0)
                _seenNS.insert(ns);
        }

        Status* s = _getStatus(addr);

        std::unique_ptr<DBClientBase> c;
        if (s->avail) {
            c.reset(s->avail);
            s->avail = 0;

            // May throw an exception
            shardConnectionPool.onHandedOut(c.get());
        } else {
            c.reset(shardConnectionPool.get(addr));

            // After, so failed creation doesn't get counted
            s->created++;
        }

        return c.release();
    }

    void releaseAll(bool fromDestructor = false) {
        // Don't need spinlock protection because if not in the destructor, we don't modify
        // _hosts, and if in the destructor we are not accessible to external threads.
        for (HostMap::iterator i = _hosts.begin(); i != _hosts.end(); ++i) {
            const auto addr = i->first;
            Status* ss = i->second;
            invariant(ss);

            if (ss->avail) {
                // If we're shutting down, don't want to initiate release mechanism as it is
                // slow, and isn't needed since all connections will be closed anyway.
                if (globalInShutdownDeprecated()) {
                    if (versionManager.isVersionableCB(ss->avail)) {
                        versionManager.resetShardVersionCB(ss->avail);
                    }

                    delete ss->avail;
                } else {
                    release(addr, ss->avail);
                }

                ss->avail = 0;
            }

            if (fromDestructor) {
                delete ss;
            }
        }

        if (fromDestructor) {
            _hosts.clear();
        }
    }

    void done(const std::string& addr, DBClientBase* conn) {
        Status* s = _hosts[addr];
        verify(s);

        const bool isConnGood = shardConnectionPool.isConnectionGood(addr, conn);

        if (s->avail != NULL) {
            warning() << "Detected additional sharded connection in the "
                      << "thread local pool for " << addr;

            if (DBException::traceExceptions.load()) {
                // There shouldn't be more than one connection checked out to the same
                // host on the same thread.
                printStackTrace();
            }

            if (!isConnGood) {
                delete s->avail;
                s->avail = NULL;
            }

            // Let the internal pool handle the bad connection, this can also
            // update the lower bounds for the known good socket creation time
            // for this host.
            release(addr, conn);
            return;
        }

        if (!isConnGood) {
            // Let the internal pool handle the bad connection.
            release(addr, conn);
            return;
        }

        // Note: Although we try our best to clear bad connections as much as possible, some of them
        // can still slip through because of how ClientConnections are being used - as thread local
        // variables. This means that threads won't be able to see the s->avail connection of other
        // threads.
        s->avail = conn;
    }

    void checkVersions(OperationContext* opCtx, const std::string& ns) {
        auto const shardRegistry = Grid::get(opCtx)->shardRegistry();

        std::vector<ShardId> all;
        shardRegistry->getAllShardIdsNoReload(&all);

        // Don't report exceptions here as errors in GetLastError
        LastError::Disabled ignoreForGLE(&LastError::get(cc()));

        // Now only check top-level shard connections
        for (const ShardId& shardId : all) {
            try {
                auto shardStatus = shardRegistry->getShard(opCtx, shardId);
                if (!shardStatus.isOK()) {
                    invariant(shardStatus == ErrorCodes::ShardNotFound);
                    continue;
                }
                const auto shard = shardStatus.getValue();

                const auto sconnString = shard->getConnString().toString();

                Status* s = _getStatus(sconnString);
                if (!s->avail) {
                    s->avail = shardConnectionPool.get(sconnString);
                    s->created++;  // After, so failed creation doesn't get counted
                }

                versionManager.checkShardVersionCB(opCtx, s->avail, ns, false, 1);
            } catch (const DBException& ex) {
                warning() << "Problem while initially checking shard versions on"
                          << " " << shardId << causedBy(redact(ex));

                // NOTE: This is only a heuristic, to avoid multiple stale version retries across
                // multiple shards, and does not affect correctness.
            }
        }
    }

    void release(const std::string& addr, DBClientBase* conn) {
        shardConnectionPool.release(addr, conn);
    }

    void forgetNS(const std::string& ns) {
        scoped_spinlock lock(_lock);
        _seenNS.erase(ns);
    }

    /**
     * Clears the connections kept by this pool (ie, not including the global pool)
     */
    void clearPool() {
        for (HostMap::iterator iter = _hosts.begin(); iter != _hosts.end(); ++iter) {
            if (iter->second->avail != NULL) {
                delete iter->second->avail;
            }
            delete iter->second;
        }

        _hosts.clear();
    }

    /**
     * Appends info about the client connection pool to a BSONObjBuilder. Safe to call with
     * activeClientConnections lock.
     */
    void appendInfo(BSONObjBuilder& b) const {
        scoped_spinlock lock(_lock);

        BSONArrayBuilder hostsArrB(b.subarrayStart("hosts"));
        for (HostMap::const_iterator i = _hosts.begin(); i != _hosts.end(); ++i) {
            BSONObjBuilder bb(hostsArrB.subobjStart());
            bb.append("host", i->first);
            bb.append("created", i->second->created);
            bb.appendBool("avail", static_cast<bool>(i->second->avail));
            bb.done();
        }
        hostsArrB.done();

        BSONArrayBuilder nsArrB(b.subarrayStart("seenNS"));
        for (const auto& ns : _seenNS) {
            nsArrB.append(ns);
        }
        nsArrB.done();
    }

private:
    /**
     * Gets or creates the status object for the host.
     */
    Status* _getStatus(const std::string& addr) {
        scoped_spinlock lock(_lock);
        Status*& temp = _hosts[addr];
        if (!temp) {
            temp = new Status();
        }

        return temp;
    }

    static thread_local std::unique_ptr<ClientConnections> _perThread;

    // Protects only the creation of new entries in the _hosts and _seenNS map from external
    // threads. Reading _hosts/_seenNS in this thread doesn't need protection.
    mutable SpinLock _lock;

    using HostMap = std::map<std::string, Status*, DBConnectionPool::serverNameCompare>;
    HostMap _hosts;
    std::set<std::string> _seenNS;
};

void ActiveClientConnections::appendInfo(BSONObjBuilder* b) const {
    // Preallocate the buffer because there may be quite a few threads to report
    BSONArrayBuilder arr(64 * 1024);

    {
        stdx::lock_guard<stdx::mutex> lock(_mutex);
        for (const auto* conn : _clientConnections) {
            BSONObjBuilder bb(arr.subobjStart());
            conn->appendInfo(bb);
            bb.doneFast();
        }
    }

    b->appendArray("threads", arr.obj());
}

thread_local std::unique_ptr<ClientConnections> ClientConnections::_perThread;

// Maximum connections per host the sharded conn pool should store
int maxShardedConnsPerHost(200);
ExportedServerParameter<int, ServerParameterType::kStartupOnly>  //
    maxShardedConnsPerHostParameter(ServerParameterSet::getGlobal(),
                                    "connPoolMaxShardedConnsPerHost",
                                    &maxShardedConnsPerHost);

// Maximum in-use connections per host in the sharded connection pool
int maxShardedInUseConnsPerHost(std::numeric_limits<int>::max());
ExportedServerParameter<int, ServerParameterType::kStartupOnly>  //
    maxShardedInUseConnsPerHostParameter(ServerParameterSet::getGlobal(),
                                         "connPoolMaxShardedInUseConnsPerHost",
                                         &maxShardedInUseConnsPerHost);

// Amount of time, in minutes, to keep idle connections in the sharded connection pool
int shardedConnPoolIdleTimeout(std::numeric_limits<int>::max());
ExportedServerParameter<int, ServerParameterType::kStartupOnly>  //
    shardedConnPoolIdleTimeoutParameter(ServerParameterSet::getGlobal(),
                                        "shardedConnPoolIdleTimeoutMinutes",
                                        &shardedConnPoolIdleTimeout);

MONGO_INITIALIZER(InitializeShardedConnectionPool)(InitializerContext* context) {
    shardConnectionPool.setName("sharded connection pool");
    shardConnectionPool.setMaxPoolSize(maxShardedConnsPerHost);
    shardConnectionPool.setMaxInUse(maxShardedInUseConnsPerHost);
    shardConnectionPool.setIdleTimeout(shardedConnPoolIdleTimeout);

    return Status::OK();
}

}  // namespace

DBConnectionPool shardConnectionPool;

ShardConnection::ShardConnection(OperationContext* opCtx,
                                 const ConnectionString& connectionString,
                                 const std::string& ns,
                                 std::shared_ptr<ChunkManager> manager)
    : _cs(connectionString), _ns(ns), _manager(manager) {
    invariant(_cs.isValid());

    // This code should never run under a cross-shard transaction
    invariant(!TransactionRouter::get(opCtx));

    // Make sure we specified a manager for the correct namespace
    if (_ns.size() && _manager) {
        invariant(_manager->getns().ns() == _ns);
    }

    auto csString = _cs.toString();

    _conn = ClientConnections::threadInstance()->get(csString, _ns);
    if (isMongos()) {
        // In mongos, we record this connection as having been used for useful work to provide
        // useful information in getLastError.
        ClusterLastErrorInfo::get(opCtx->getClient())->addShardHost(csString);
    }
}

ShardConnection::~ShardConnection() {
    if (_conn) {
        if (_conn->isFailed()) {
            if (_conn->getSockCreationMicroSec() == DBClientBase::INVALID_SOCK_CREATION_TIME) {
                kill();
            } else {
                // The pool takes care of deleting the failed connection - this
                // will also trigger disposal of older connections in the pool
                done();
            }
        } else {
            // see done() comments above for why we log this line
            log() << "sharded connection to " << _conn->getServerAddress()
                  << " not being returned to the pool";

            kill();
        }
    }
}

void ShardConnection::_finishInit() {
    if (_finishedInit)
        return;
    _finishedInit = true;

    if (versionManager.isVersionableCB(_conn)) {
        auto& client = cc();
        auto opCtx = client.getOperationContext();
        invariant(opCtx);
        _setVersion = versionManager.checkShardVersionCB(opCtx, this, false, 1);
    } else {
        // Make sure we didn't specify a manager for a non-versionable connection (i.e. config)
        invariant(!_manager);
        _setVersion = false;
    }
}

void ShardConnection::done() {
    if (_conn) {
        ClientConnections::threadInstance()->done(_cs.toString(), _conn);
        _conn = nullptr;
        _finishedInit = true;
    }
}

void ShardConnection::kill() {
    if (_conn) {
        if (versionManager.isVersionableCB(_conn)) {
            versionManager.resetShardVersionCB(_conn);
        }

        if (_conn->isFailed()) {
            // Let the pool know about the bad connection and also delegate disposal to it.
            ClientConnections::threadInstance()->done(_cs.toString(), _conn);
        } else {
            delete _conn;
        }

        _conn = 0;
        _finishedInit = true;
    }
}

void ShardConnection::reportActiveClientConnections(BSONObjBuilder* builder) {
    activeClientConnections.appendInfo(builder);
}

void ShardConnection::checkMyConnectionVersions(OperationContext* opCtx, const std::string& ns) {
    ClientConnections::threadInstance()->checkVersions(opCtx, ns);
}

void ShardConnection::releaseMyConnections() {
    ClientConnections::threadInstance()->releaseAll();
}

void ShardConnection::clearPool() {
    shardConnectionPool.clear();
    ClientConnections::threadInstance()->clearPool();
}

void ShardConnection::forgetNS(const std::string& ns) {
    ClientConnections::threadInstance()->forgetNS(ns);
}

}  // namespace mongo
コード例 #28
0
ファイル: connpool.cpp プロジェクト: elenno/MyServer
namespace mongo {

    // ------ PoolForHost ------

    PoolForHost::~PoolForHost() {
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            delete sc.conn;
            _pool.pop();
        }
    }

    void PoolForHost::done( DBClientBase * c ) {
        if ( _pool.size() >= _maxPerHost ) {
            delete c;
        }
        else {
            _pool.push(c);
        }
    }

    DBClientBase * PoolForHost::get() {

        time_t now = time(0);

        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            _pool.pop();
            if ( sc.ok( now ) )
                return sc.conn;
            delete sc.conn;
        }

        return NULL;
    }

    void PoolForHost::flush() {
        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            all.push_back( c );
            bool res;
            c.conn->isMaster( res );
        }

        for ( vector<StoredConnection>::iterator i=all.begin(); i != all.end(); ++i ) {
            _pool.push( *i );
        }
    }

    PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ) {
        conn = c;
        when = time(0);
    }

    bool PoolForHost::StoredConnection::ok( time_t now ) {
        // if connection has been idle for an hour, kill it
        return ( now - when ) < 3600;
    }

    void PoolForHost::createdOne( DBClientBase * base) {
        if ( _created == 0 )
            _type = base->type();
        _created++;
    }

    unsigned PoolForHost::_maxPerHost = 50;

    // ------ DBConnectionPool ------

    DBConnectionPool pool;

    DBClientBase* DBConnectionPool::_get(const string& ident) {
        scoped_lock L(_mutex);
        PoolForHost& p = _pools[ident];
        return p.get();
    }

    DBClientBase* DBConnectionPool::_finishCreate( const string& host , DBClientBase* conn ) {
        {
            scoped_lock L(_mutex);
            PoolForHost& p = _pools[host];
            p.createdOne( conn );
        }

        onCreate( conn );
        onHandedOut( conn );

        return conn;
    }

    DBClientBase* DBConnectionPool::get(const ConnectionString& url) {
        DBClientBase * c = _get( url.toString() );
        if ( c ) {
            onHandedOut( c );
            return c;
        }

        string errmsg;
        c = url.connect( errmsg );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );

        return _finishCreate( url.toString() , c );
    }

    DBClientBase* DBConnectionPool::get(const string& host) {
        DBClientBase * c = _get( host );
        if ( c ) {
            onHandedOut( c );
            return c;
        }

        string errmsg;
        ConnectionString cs = ConnectionString::parse( host , errmsg );
        uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );

        c = cs.connect( errmsg );
        if ( ! c )
            throw SocketException( SocketException::CONNECT_ERROR , host , 11002 , str::stream() << _name << " error: " << errmsg );
        return _finishCreate( host , c );
    }

    DBConnectionPool::~DBConnectionPool() {
        // connection closing is handled by ~PoolForHost
    }

    void DBConnectionPool::flush() {
        scoped_lock L(_mutex);
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++ ) {
            PoolForHost& p = i->second;
            p.flush();
        }
    }

    void DBConnectionPool::addHook( DBConnectionHook * hook ) {
        _hooks.push_back( hook );
    }

    void DBConnectionPool::onCreate( DBClientBase * conn ) {
        if ( _hooks.size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks.begin(); i != _hooks.end(); i++ ) {
            (*i)->onCreate( conn );
        }
    }

    void DBConnectionPool::onHandedOut( DBClientBase * conn ) {
        if ( _hooks.size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks.begin(); i != _hooks.end(); i++ ) {
            (*i)->onHandedOut( conn );
        }
    }

    void DBConnectionPool::appendInfo( BSONObjBuilder& b ) {
        BSONObjBuilder bb( b.subobjStart( "hosts" ) );
        int avail = 0;
        long long created = 0;


        map<ConnectionString::ConnectionType,long long> createdByType;

        {
            scoped_lock lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                if ( i->second.numCreated() == 0 )
                    continue;

                string s = i->first;
                BSONObjBuilder temp( bb.subobjStart( s ) );
                temp.append( "available" , i->second.numAvailable() );
                temp.appendNumber( "created" , i->second.numCreated() );
                temp.done();

                avail += i->second.numAvailable();
                created += i->second.numCreated();

                long long& x = createdByType[i->second.type()];
                x += i->second.numCreated();
            }
        }
        bb.done();

        {
            BSONObjBuilder temp( bb.subobjStart( "createdByType" ) );
            for ( map<ConnectionString::ConnectionType,long long>::iterator i=createdByType.begin(); i!=createdByType.end(); ++i ) {
                temp.appendNumber( ConnectionString::typeToString( i->first ) , i->second );
            }
            temp.done();
        }

        b.append( "totalAvailable" , avail );
        b.appendNumber( "totalCreated" , created );
    }

    bool DBConnectionPool::serverNameCompare::operator()( const string& a , const string& b ) const{
        string ap = str::before( a , "/" );
        string bp = str::before( b , "/" );
        
        return ap < bp;
    }

    // ------ ScopedDbConnection ------

    ScopedDbConnection * ScopedDbConnection::steal() {
        assert( _conn );
        ScopedDbConnection * n = new ScopedDbConnection( _host , _conn );
        _conn = 0;
        return n;
    }

    ScopedDbConnection::~ScopedDbConnection() {
        if ( _conn ) {
            if ( ! _conn->isFailed() ) {
                /* see done() comments above for why we log this line */
                log() << "~ScopedDbConnection: _conn != null" << endl;
            }
            kill();
        }
    }

    ScopedDbConnection::ScopedDbConnection(const Shard& shard )
        : _host( shard.getConnString() ) , _conn( pool.get(_host) ) {
    }

    ScopedDbConnection::ScopedDbConnection(const Shard* shard )
        : _host( shard->getConnString() ) , _conn( pool.get(_host) ) {
    }


    class PoolFlushCmd : public Command {
    public:
        PoolFlushCmd() : Command( "connPoolSync" , false , "connpoolsync" ) {}
        virtual void help( stringstream &help ) const { help<<"internal"; }
        virtual LockType locktype() const { return NONE; }
        virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool) {
            pool.flush();
            return true;
        }
        virtual bool slaveOk() const {
            return true;
        }

    } poolFlushCmd;

    class PoolStats : public Command {
    public:
        PoolStats() : Command( "connPoolStats" ) {}
        virtual void help( stringstream &help ) const { help<<"stats about connection pool"; }
        virtual LockType locktype() const { return NONE; }
        virtual bool run(const string&, mongo::BSONObj&, std::string&, mongo::BSONObjBuilder& result, bool) {
            pool.appendInfo( result );
            result.append( "numDBClientConnection" , DBClientConnection::getNumConnections() );
            result.append( "numAScopedConnection" , AScopedConnection::getNumConnections() );
            return true;
        }
        virtual bool slaveOk() const {
            return true;
        }

    } poolStatsCmd;

    AtomicUInt AScopedConnection::_numConnections;

} // namespace mongo
コード例 #29
0
void ShardConnection::clearPool() {
    shardConnectionPool.clear();
    ClientConnections::threadInstance()->clearPool();
}
コード例 #30
0
ファイル: connpool.cpp プロジェクト: 7segments/mongo-1
namespace mongo {

    using std::endl;
    using std::list;
    using std::map;
    using std::set;
    using std::string;
    using std::vector;

    // ------ PoolForHost ------

    PoolForHost::~PoolForHost() {
        clear();
    }

    void PoolForHost::clear() {
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            delete sc.conn;
            _pool.pop();
        }
    }

    void PoolForHost::done(DBConnectionPool* pool, DBClientBase* c) {

        bool isFailed = c->isFailed();

        // Remember that this host had a broken connection for later
        if (isFailed) reportBadConnectionAt(c->getSockCreationMicroSec());

        if (isFailed ||
            // Another (later) connection was reported as broken to this host
            (c->getSockCreationMicroSec() < _minValidCreationTimeMicroSec) ||
            // We have a pool size that we need to enforce
            (_maxPoolSize >= 0 && static_cast<int>(_pool.size()) >= _maxPoolSize)) {
            pool->onDestroy(c);
            delete c;
        }
        else {
            // The connection is probably fine, save for later
            _pool.push(c);
        }
    }

    void PoolForHost::reportBadConnectionAt(uint64_t microSec) {
        if (microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
                microSec > _minValidCreationTimeMicroSec) {
            _minValidCreationTimeMicroSec = microSec;
            log() << "Detected bad connection created at " << _minValidCreationTimeMicroSec
                    << " microSec, clearing pool for " << _hostName
                    << " of " << _pool.size() << " connections" << endl;
            clear();
        }
    }

    bool PoolForHost::isBadSocketCreationTime(uint64_t microSec) {
        return microSec != DBClientBase::INVALID_SOCK_CREATION_TIME &&
                microSec <= _minValidCreationTimeMicroSec;
    }

    DBClientBase * PoolForHost::get( DBConnectionPool * pool , double socketTimeout ) {

        time_t now = time(0);
        
        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();
            _pool.pop();
            
            if ( ! sc.ok( now ) )  {
                pool->onDestroy( sc.conn );
                delete sc.conn;
                continue;
            }
            
            verify( sc.conn->getSoTimeout() == socketTimeout );

            return sc.conn;

        }

        return NULL;
    }

    void PoolForHost::flush() {
        while (!_pool.empty()) {
            StoredConnection c = _pool.top();
            _pool.pop();
            delete c.conn;
        }
    }

    void PoolForHost::getStaleConnections( vector<DBClientBase*>& stale ) {
        time_t now = time(0);

        vector<StoredConnection> all;
        while ( ! _pool.empty() ) {
            StoredConnection c = _pool.top();
            _pool.pop();
            
            if ( c.ok( now ) )
                all.push_back( c );
            else
                stale.push_back( c.conn );
        }

        for ( size_t i=0; i<all.size(); i++ ) {
            _pool.push( all[i] );
        }
    }


    PoolForHost::StoredConnection::StoredConnection( DBClientBase * c ) {
        conn = c;
        when = time(0);
    }

    bool PoolForHost::StoredConnection::ok( time_t now ) {
        // Poke the connection to see if we're still ok
        return conn->isStillConnected();
    }

    void PoolForHost::createdOne( DBClientBase * base) {
        if ( _created == 0 )
            _type = base->type();
        _created++;
    }

    void PoolForHost::initializeHostName(const std::string& hostName) {
        if (_hostName.empty()) {
            _hostName = hostName;
        }
    }

    // ------ DBConnectionPool ------

    DBConnectionPool pool;

    const int PoolForHost::kPoolSizeUnlimited(-1);

    DBConnectionPool::DBConnectionPool()
        : _name( "dbconnectionpool" ) , 
          _maxPoolSize(PoolForHost::kPoolSizeUnlimited) ,
          _hooks( new list<DBConnectionHook*>() ) {
    }

    DBClientBase* DBConnectionPool::_get(const string& ident , double socketTimeout ) {
        uassert(17382, "Can't use connection pool during shutdown",
                !inShutdown());
        boost::lock_guard<boost::mutex> L(_mutex);
        PoolForHost& p = _pools[PoolKey(ident,socketTimeout)];
        p.setMaxPoolSize(_maxPoolSize);
        p.initializeHostName(ident);
        return p.get( this , socketTimeout );
    }

    DBClientBase* DBConnectionPool::_finishCreate( const string& host , double socketTimeout , DBClientBase* conn ) {
        {
            boost::lock_guard<boost::mutex> L(_mutex);
            PoolForHost& p = _pools[PoolKey(host,socketTimeout)];
            p.setMaxPoolSize(_maxPoolSize);
            p.initializeHostName(host);
            p.createdOne( conn );
        }
        
        try {
            onCreate( conn );
            onHandedOut( conn );
        }
        catch ( std::exception & ) {
            delete conn;
            throw;
        }

        return conn;
    }

    DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
        DBClientBase * c = _get( url.toString() , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        c = url.connect( errmsg, socketTimeout );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );

        return _finishCreate( url.toString() , socketTimeout , c );
    }

    DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
        DBClientBase * c = _get( host , socketTimeout );
        if ( c ) {
            try {
                onHandedOut( c );
            }
            catch ( std::exception& ) {
                delete c;
                throw;
            }
            return c;
        }

        string errmsg;
        ConnectionString cs = ConnectionString::parse( host , errmsg );
        uassert( 13071 , (string)"invalid hostname [" + host + "]" + errmsg , cs.isValid() );

        c = cs.connect( errmsg, socketTimeout );
        if ( ! c )
            throw SocketException( SocketException::CONNECT_ERROR , host , 11002 , str::stream() << _name << " error: " << errmsg );
        return _finishCreate( host , socketTimeout , c );
    }

    void DBConnectionPool::onRelease(DBClientBase* conn) {
        if (_hooks->empty()) {
            return;
        }

        for (list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++) {
            (*i)->onRelease( conn );
        }
    }

    void DBConnectionPool::release(const string& host, DBClientBase *c) {
        onRelease(c);

        boost::lock_guard<boost::mutex> L(_mutex);
        _pools[PoolKey(host,c->getSoTimeout())].done(this,c);
    }


    DBConnectionPool::~DBConnectionPool() {
        // connection closing is handled by ~PoolForHost
    }

    void DBConnectionPool::flush() {
        boost::lock_guard<boost::mutex> L(_mutex);
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); i++ ) {
            PoolForHost& p = i->second;
            p.flush();
        }
    }

    void DBConnectionPool::clear() {
        boost::lock_guard<boost::mutex> L(_mutex);
        LOG(2) << "Removing connections on all pools owned by " << _name  << endl;
        for (PoolMap::iterator iter = _pools.begin(); iter != _pools.end(); ++iter) {
            iter->second.clear();
        }
    }

    void DBConnectionPool::removeHost( const string& host ) {
        boost::lock_guard<boost::mutex> L(_mutex);
        LOG(2) << "Removing connections from all pools for host: " << host << endl;
        for ( PoolMap::iterator i = _pools.begin(); i != _pools.end(); ++i ) {
            const string& poolHost = i->first.ident;
            if ( !serverNameCompare()(host, poolHost) && !serverNameCompare()(poolHost, host) ) {
                // hosts are the same
                i->second.clear();
            }
        }
    }

    void DBConnectionPool::addHook( DBConnectionHook * hook ) {
        _hooks->push_back( hook );
    }

    void DBConnectionPool::onCreate( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onCreate( conn );
        }
    }

    void DBConnectionPool::onHandedOut( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onHandedOut( conn );
        }
    }

    void DBConnectionPool::onDestroy( DBClientBase * conn ) {
        if ( _hooks->size() == 0 )
            return;

        for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
            (*i)->onDestroy( conn );
        }
    }

    void DBConnectionPool::appendInfo( BSONObjBuilder& b ) {

        int avail = 0;
        long long created = 0;


        map<ConnectionString::ConnectionType,long long> createdByType;
        
        BSONObjBuilder bb( b.subobjStart( "hosts" ) );
        {
            boost::lock_guard<boost::mutex> lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                if ( i->second.numCreated() == 0 )
                    continue;

                string s = str::stream() << i->first.ident << "::" << i->first.timeout;

                BSONObjBuilder temp( bb.subobjStart( s ) );
                temp.append( "available" , i->second.numAvailable() );
                temp.appendNumber( "created" , i->second.numCreated() );
                temp.done();

                avail += i->second.numAvailable();
                created += i->second.numCreated();

                long long& x = createdByType[i->second.type()];
                x += i->second.numCreated();
            }
        }
        bb.done();
        
        // Always report all replica sets being tracked
        set<string> replicaSets = ReplicaSetMonitor::getAllTrackedSets();
        
        BSONObjBuilder setBuilder( b.subobjStart( "replicaSets" ) );
        for ( set<string>::iterator i=replicaSets.begin(); i!=replicaSets.end(); ++i ) {
            string rs = *i;
            ReplicaSetMonitorPtr m = ReplicaSetMonitor::get( rs );
            if ( ! m ) {
                warning() << "no monitor for set: " << rs << endl;
                continue;
            }
            
            BSONObjBuilder temp( setBuilder.subobjStart( rs ) );
            m->appendInfo( temp );
            temp.done();
        }
        setBuilder.done();

        {
            BSONObjBuilder temp( bb.subobjStart( "createdByType" ) );
            for ( map<ConnectionString::ConnectionType,long long>::iterator i=createdByType.begin(); i!=createdByType.end(); ++i ) {
                temp.appendNumber( ConnectionString::typeToString( i->first ) , i->second );
            }
            temp.done();
        }

        b.append( "totalAvailable" , avail );
        b.appendNumber( "totalCreated" , created );
    }

    bool DBConnectionPool::serverNameCompare::operator()( const string& a , const string& b ) const{
        const char* ap = a.c_str();
        const char* bp = b.c_str();
       
        while (true){
            if (*ap == '\0' || *ap == '/'){
                if (*bp == '\0' || *bp == '/')
                    return false; // equal strings
                else
                    return true; // a is shorter
            }

            if (*bp == '\0' || *bp == '/')
                return false; // b is shorter
            
            if ( *ap < *bp)
                return true;
            else if (*ap > *bp)
                return false;

            ++ap;
            ++bp;
        }
        verify(false);
    }
    
    bool DBConnectionPool::poolKeyCompare::operator()( const PoolKey& a , const PoolKey& b ) const {
        if (DBConnectionPool::serverNameCompare()( a.ident , b.ident ))
            return true;
        
        if (DBConnectionPool::serverNameCompare()( b.ident , a.ident ))
            return false;

        return a.timeout < b.timeout;
    }

    bool DBConnectionPool::isConnectionGood(const string& hostName, DBClientBase* conn) {
        if (conn == NULL) {
            return false;
        }

        if (conn->isFailed()) {
            return false;
        }

        {
            boost::lock_guard<boost::mutex> sl(_mutex);
            PoolForHost& pool = _pools[PoolKey(hostName, conn->getSoTimeout())];
            if (pool.isBadSocketCreationTime(conn->getSockCreationMicroSec())) {
                return false;
            }
        }

        return true;
    }

    void DBConnectionPool::taskDoWork() { 
        vector<DBClientBase*> toDelete;
        
        {
            // we need to get the connections inside the lock
            // but we can actually delete them outside
            boost::lock_guard<boost::mutex> lk( _mutex );
            for ( PoolMap::iterator i=_pools.begin(); i!=_pools.end(); ++i ) {
                i->second.getStaleConnections( toDelete );
            }
        }

        for ( size_t i=0; i<toDelete.size(); i++ ) {
            try {
                onDestroy( toDelete[i] );
                delete toDelete[i];
            }
            catch ( ... ) {
                // we don't care if there was a socket error
            }
        }
    }

    // ------ ScopedDbConnection ------

    void ScopedDbConnection::_setSocketTimeout(){
        if( ! _conn ) return;
        if( _conn->type() == ConnectionString::MASTER )
            (( DBClientConnection* ) _conn)->setSoTimeout( _socketTimeout );
        else if( _conn->type() == ConnectionString::SYNC )
            (( SyncClusterConnection* ) _conn)->setAllSoTimeouts( _socketTimeout );
    }

    ScopedDbConnection::~ScopedDbConnection() {
        if ( _conn ) {
            if (_conn->isFailed()) {
                if (_conn->getSockCreationMicroSec() ==
                        DBClientBase::INVALID_SOCK_CREATION_TIME) {
                    kill();
                }
                else {
                    // The pool takes care of deleting the failed connection - this
                    // will also trigger disposal of older connections in the pool
                    done();
                }
            }
            else {
                /* see done() comments above for why we log this line */
                log() << "scoped connection to " << _conn->getServerAddress()
                        << " not being returned to the pool" << endl;
                kill();
            }
        }
    }

    void ScopedDbConnection::clearPool() {
        pool.clear();
    }

    AtomicInt32 AScopedConnection::_numConnections;

} // namespace mongo