Пример #1
0
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;
}
Пример #2
0
    Database* DatabaseHolder::openDb(OperationContext* txn,
                                     StringData ns,
                                     bool* justCreated) {

        const StringData dbname = _todb(ns);
        invariant(txn->lockState()->isDbLockedForMode(dbname, MODE_X));

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

            return db;
        }

        // Check casing
        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);
        const bool exists = entry->exists();
        if (!exists) {
            audit::logCreateDatabase(currentClient.get(), dbname);
        }

        if (justCreated) {
            *justCreated = !exists;
        }

        // Do this outside of the scoped lock, because database creation does transactional
        // operations which 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.
        db = new Database(txn, dbname, entry);

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

        return db;
    }
Пример #3
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;
    }
Пример #4
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;
}