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; }
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; }
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; }
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; }
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; }
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() ); }
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; }
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; }
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; }
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; }