Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) {
    const StringData dbname = _todb(ns);
    invariant(opCtx->lockState()->isDbLockedForMode(dbname, MODE_X));

    if (justCreated)
        *justCreated = false;  // Until proven otherwise.

    stdx::unique_lock<SimpleMutex> lk(_m);

    // The following will insert a nullptr for dbname, which will treated the same as a non-
    // existant database by the get method, yet still counts in getNamesWithConflictingCasing.
    if (auto db = _dbs[dbname])
        return db;

    // We've inserted a nullptr entry for dbname: make sure to remove it on unsuccessful exit.
    auto removeDbGuard = makeGuard([this, &lk, dbname] {
        if (!lk.owns_lock())
            lk.lock();
        _dbs.erase(dbname);
    });

    // Check casing in lock to avoid transient duplicates.
    auto duplicates = _getNamesWithConflictingCasing_inlock(dbname);
    uassert(ErrorCodes::DatabaseDifferCase,
            str::stream() << "db already exists with different case already have: ["
                          << *duplicates.cbegin()
                          << "] trying to create ["
                          << dbname.toString()
                          << "]",
            duplicates.empty());


    // Do the catalog lookup and database creation outside of the scoped lock, because these may
    // block. Only one thread can be inside this method for the same DB name, because of the
    // requirement for X-lock on the database when we enter. So there is no way we can insert two
    // different databases for the same name.
    lk.unlock();
    StorageEngine* storageEngine = getGlobalServiceContext()->getStorageEngine();
    DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(opCtx, dbname);

    if (!entry->exists()) {
        audit::logCreateDatabase(opCtx->getClient(), dbname);
        if (justCreated)
            *justCreated = true;
    }

    auto newDb = stdx::make_unique<DatabaseImpl>(dbname, entry, ++_epoch);
    newDb->init(opCtx);

    // Finally replace our nullptr entry with the new Database pointer.
    removeDbGuard.dismiss();
    lk.lock();
    auto it = _dbs.find(dbname);
    invariant(it != _dbs.end() && it->second == nullptr);
    it->second = newDb.release();
    invariant(_getNamesWithConflictingCasing_inlock(dbname.toString()).empty());

    return it->second;
}
Database* DatabaseHolderImpl::getDb(OperationContext* opCtx, StringData ns) const {
    const StringData db = _todb(ns);
    invariant(opCtx->lockState()->isDbLockedForMode(db, MODE_IS));

    stdx::lock_guard<SimpleMutex> lk(_m);
    DBs::const_iterator it = _dbs.find(db);
    if (it != _dbs.end()) {
        return it->second;
    }

    return NULL;
}
Exemple #3
0
Database* DatabaseHolder::get(OperationContext* txn,
                              const StringData& ns) const {

    const StringData db = _todb( ns );
    txn->lockState()->assertAtLeastReadLocked(db);

    SimpleMutex::scoped_lock lk(_m);
    DBs::const_iterator it = _dbs.find(db);
    if ( it != _dbs.end() )
        return it->second;
    return NULL;
}
Exemple #4
0
    Database* DatabaseHolder::getOrCreate(OperationContext* txn,
                                          const string& ns,
                                          bool& justCreated) {

        const string dbname = _todb( ns );
        invariant(txn->lockState()->isAtLeastReadLocked(dbname));

        if (txn->lockState()->isWriteLocked() && FileAllocator::get()->hasFailed()) {
            uassert(17507, "Can't take a write lock while out of disk space", false);
        }

        {
            SimpleMutex::scoped_lock lk(_m);
            {
                DBs::iterator i = _dbs.find(dbname);
                if( i != _dbs.end() ) {
                    justCreated = false;
                    return i->second;
                }
            }

            // todo: protect against getting sprayed with requests for different db names that DNE -
            //       that would make the DBs map very large.  not clear what to do to handle though,
            //       perhaps just log it, which is what we do here with the "> 40" :
            bool cant = !txn->lockState()->isWriteLocked(ns);
            if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) ||
                _dbs.size() > 40 || cant || DEBUG_BUILD ) {
                log() << "opening db: " << dbname;
            }
            massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant);
        }

        // we mark our thread as having done writes now as we do not want any exceptions
        // once we start creating a new database
        cc().writeHappened();

        // this locks _m for defensive checks, so we don't want to be locked right here :
        StorageEngine* storageEngine = getGlobalEnvironment()->getGlobalStorageEngine();
        invariant(storageEngine);
        DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry( txn, dbname );
        invariant( entry );
        justCreated = !entry->exists();
        Database *db = new Database(txn,
                                    dbname,
                                    entry );

        {
            SimpleMutex::scoped_lock lk(_m);
            _dbs[dbname] = db;
        }

        return db;
    }
Exemple #5
0
    Database* DatabaseHolder::getOrCreate(
                OperationContext* txn, const string& ns, const string& path, bool& justCreated) {

        const string dbname = _todb( ns );
        invariant(txn->lockState()->isAtLeastReadLocked(dbname));

        if (txn->lockState()->hasAnyWriteLock() && FileAllocator::get()->hasFailed()) {
            uassert(17507, "Can't take a write lock while out of disk space", false);
        }

        {
            SimpleMutex::scoped_lock lk(_m);
            DBs& m = _paths[path];
            {
                DBs::iterator i = m.find(dbname);
                if( i != m.end() ) {
                    justCreated = false;
                    return i->second;
                }
            }

            // todo: protect against getting sprayed with requests for different db names that DNE -
            //       that would make the DBs map very large.  not clear what to do to handle though,
            //       perhaps just log it, which is what we do here with the "> 40" :
            bool cant = !txn->lockState()->isWriteLocked(ns);
            if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) ||
                m.size() > 40 || cant || DEBUG_BUILD ) {
                log() << "opening db: "
                      << (path == storageGlobalParams.dbpath ? "" : path) << ' ' << dbname
                      << endl;
            }
            massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant);
        }

        // we mark our thread as having done writes now as we do not want any exceptions
        // once we start creating a new database
        cc().writeHappened();

        // this locks _m for defensive checks, so we don't want to be locked right here :
        Database *db = new Database(txn, dbname.c_str(), justCreated, path);

        {
            SimpleMutex::scoped_lock lk(_m);
            DBs& m = _paths[path];
            verify( m[dbname] == 0 );
            m[dbname] = db;
            _size++;
        }

        return db;
    }
Exemple #6
0
Database* DatabaseHolder::getOrCreate(OperationContext* txn,
                                      const StringData& ns,
                                      bool& justCreated) {

    const StringData dbname = _todb( ns );
    invariant(txn->lockState()->isAtLeastReadLocked(dbname));

    Database* db = get(txn, ns);
    if (db) {
        justCreated = false;
        return db;
    }

    // todo: protect against getting sprayed with requests for different db names that DNE -
    //       that would make the DBs map very large.  not clear what to do to handle though,
    //       perhaps just log it, which is what we do here with the "> 40" :
    bool cant = !txn->lockState()->isWriteLocked(ns);
    if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) ||
            _dbs.size() > 40 || cant || DEBUG_BUILD ) {
        log() << "opening db: " << dbname;
    }

    massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant);

    const string duplicate = Database::duplicateUncasedName(dbname.toString());
    if ( !duplicate.empty() ) {
        stringstream ss;
        ss << "db already exists with different case already have: ["
           << duplicate
           << "] trying to create ["
           << dbname.toString()
           << "]";
        uasserted( DatabaseDifferCaseCode , ss.str() );
    }
    StorageEngine* storageEngine = getGlobalEnvironment()->getGlobalStorageEngine();
    invariant(storageEngine);

    DatabaseCatalogEntry* entry = storageEngine->getDatabaseCatalogEntry(txn, dbname);
    invariant(entry);
    justCreated = !entry->exists();

    db = new Database(dbname, entry);

    {
        SimpleMutex::scoped_lock lk(_m);
        _dbs[dbname] = db;
    }

    return db;
}
Exemple #7
0
void DatabaseHolder::close(OperationContext* txn,
                           const StringData& ns) {
    invariant(txn->lockState()->isW());

    StringData db = _todb(ns);

    SimpleMutex::scoped_lock lk(_m);
    DBs::const_iterator it = _dbs.find(db);
    if ( it == _dbs.end() )
        return;

    it->second->close( txn );
    delete it->second;
    _dbs.erase( db );

    getGlobalEnvironment()->getGlobalStorageEngine()->closeDatabase( txn, db.toString() );
}
Exemple #8
0
    Database* DatabaseHolder::getOrCreate( const string& ns, const string& path, bool& justCreated ) {
        string dbname = _todb( ns );
        {
            SimpleMutex::scoped_lock lk(_m);
            Lock::assertAtLeastReadLocked(ns);
            DBs& m = _paths[path];
            {
                DBs::iterator i = m.find(dbname);
                if( i != m.end() ) {
                    justCreated = false;
                    return i->second;
                }
            }

            // todo: protect against getting sprayed with requests for different db names that DNE -
            //       that would make the DBs map very large.  not clear what to do to handle though,
            //       perhaps just log it, which is what we do here with the "> 40" :
            bool cant = !Lock::isWriteLocked(ns);
            if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) ||
                m.size() > 40 || cant || DEBUG_BUILD ) {
                log() << "opening db: " << (path==dbpath?"":path) << ' ' << dbname << endl;
            }
            massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant);
        }

        // we mark our thread as having done writes now as we do not want any exceptions
        // once we start creating a new database
        cc().writeHappened();

        // this locks _m for defensive checks, so we don't want to be locked right here :
        Database *db = new Database( dbname.c_str() , justCreated , path );

        {
            SimpleMutex::scoped_lock lk(_m);
            DBs& m = _paths[path];
            verify( m[dbname] == 0 );
            m[dbname] = db;
            _size++;
        }

        authindex::configureSystemIndexes(dbname);

        db->clearTmpCollections();

        return db;
    }
Exemple #9
0
    Database* DatabaseHolder::getOrCreate( const string& ns , const string& path , bool& justCreated ) {
        dbMutex.assertWriteLocked();
        DBs& m = _paths[path];

        string dbname = _todb( ns );

        Database* & db = m[dbname];
        if ( db ) {
            justCreated = false;
            return db;
        }

        log(1) << "Accessing: " << dbname << " for the first time" << endl;
        try {
            db = new Database( dbname.c_str() , justCreated , path );
        }
        catch ( ... ) {
            m.erase( dbname );
            throw;
        }
        _size++;
        return db;
    }
Exemple #10
0
    Database* DatabaseHolder::getOrCreate( const string& ns , const string& path , bool& justCreated ) {
        string dbname = _todb( ns );
        {
            SimpleMutex::scoped_lock lk(_m);
            Lock::assertAtLeastReadLocked(ns);
            DBs& m = _paths[path];
            {
                DBs::iterator i = m.find(dbname); 
                if( i != m.end() ) {
                    justCreated = false;
                    return i->second;
                }
            }

            // todo: protect against getting sprayed with requests for different db names that DNE - 
            //       that would make the DBs map very large.  not clear what to do to handle though, 
            //       perhaps just log it, which is what we do here with the "> 40" : 
            bool cant = !Lock::isWriteLocked(ns);
            if( logLevel >= 1 || m.size() > 40 || cant || DEBUG_BUILD ) {
                log() << "opening db: " << (path==dbpath?"":path) << ' ' << dbname << endl;
            }
            massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant);
        }

        // this locks _m for defensive checks, so we don't want to be locked right here : 
        Database *db = new Database( dbname.c_str() , justCreated , path );

        {
            SimpleMutex::scoped_lock lk(_m);
            DBs& m = _paths[path];
            assert( m[dbname] == 0 );
            m[dbname] = db;
            _size++;
        }

        return db;
    }
Exemple #11
0
    Database* DatabaseHolder::getOrCreate( const string& ns , const string& path , bool& justCreated ) {
        dbMutex.assertAtLeastReadLocked();

        // note the full db opening, not just the map lookup, needs to be done in this lock
        recursive_scoped_lock lk(dbHolderMutex);

        DBs& m = _paths[path];

        string dbname = _todb( ns );

        {
            DBs::iterator i = m.find(dbname); 
            if( i != m.end() ) {
                justCreated = false;
                return i->second;
            }
        }

        log(1) << "Accessing: " << dbname << " for the first time" << endl;
        Database *db = new Database( dbname.c_str() , justCreated , path );
        m[dbname] = db;
        _size++;
        return db;
    }