diff --git a/db/db.h b/db/db.h index 4369a3dca30..87e157ab1ed 100644 --- a/db/db.h +++ b/db/db.h @@ -40,24 +40,80 @@ namespace mongo { could be slightly larger. */ const int MaxBSONObjectSize = 4 * 1024 * 1024; + + /** + * class to hold path + dbname -> Database + * might be able to optimizer further + */ + class DatabaseHolder { + public: + DatabaseHolder() : _size(0){ + } - // tempish...move to TLS or pass all the way down as a parm - extern map databases; - extern bool master; + Database * get( const string& ns , const string& path ){ + dbMutex.assertAtLeastReadLocked(); + map& m = _paths[path]; + + string db = _todb( ns ); - /* sometimes we deal with databases with the same name in different directories - thus this */ - inline string makeDbKeyStr( const char *ns, const string& path ) { - char cl[256]; - nsToDatabase(ns, cl); - return string( cl ) + ":" + path; - } + map::iterator it = m.find(db); + if ( it != m.end() ) + return it->second; + return 0; + } + + void put( const string& ns , const string& path , Database * db ){ + dbMutex.assertWriteLocked(); + map& m = _paths[path]; + Database*& d = m[_todb(ns)]; + if ( ! d ) + _size++; + d = db; + } + + void erase( const string& ns , const string& path ){ + dbMutex.assertWriteLocked(); + map& m = _paths[path]; + _size -= m.erase( _todb( ns ) ); + } + + bool closeAll( const string& path , BSONObjBuilder& result ); + + int size(){ + return _size; + } + + void getAllShortNames( set& all ) const{ + dbMutex.assertAtLeastReadLocked(); + for ( map >::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ){ + map m = i->second; + for( map::const_iterator j=m.begin(); j!=m.end(); j++ ){ + all.insert( j->first ); + } + } + } + + private: + + string _todb( const string& ns ){ + size_t i = ns.find( '.' ); + if ( i == string::npos ) + return ns; + return ns.substr( 0 , i ); + } + + map > _paths; + int _size; + + }; + + extern DatabaseHolder dbHolder; inline void resetClient(const char *ns, const string& path=dbpath) { dbMutex.assertAtLeastReadLocked(); - string key = makeDbKeyStr( ns, path ); - map::iterator it = databases.find(key); - if ( it != databases.end() ) { - cc().setns(ns, it->second); + Database * d = dbHolder.get( ns , path ); + if ( d ){ + cc().setns(ns, d); return; } assert(false); @@ -76,32 +132,22 @@ namespace mongo { Client& c = cc(); c.top.clientStart( ns ); - string key = makeDbKeyStr( ns, path ); - map::iterator it = databases.find(key); - if ( it != databases.end() ) { - c.setns(ns, it->second); + Database * db = dbHolder.get( ns , path ); + if ( db ){ + c.setns(ns, db ); return false; } if( lock ) lock->releaseAndWriteLock(); - // when master for replication, we advertise all the db's, and that - // looks like a 'first operation'. so that breaks this log message's - // meaningfulness. instead of fixing (which would be better), we just - // stop showing for now. - // 2008-12-22 We now open every database on startup, so this log is - // no longer helpful. Commenting. - // if( !master ) - // log() << "first operation for database " << key << endl; - assertInWriteLock(); - + char cl[256]; nsToDatabase(ns, cl); bool justCreated; Database *newdb = new Database(cl, justCreated, path); - databases[key] = newdb; + dbHolder.put(ns,path,newdb); c.setns(ns, newdb); newdb->finishInit(); @@ -109,16 +155,10 @@ namespace mongo { return justCreated; } -// shared functionality for removing references to a database from this program instance -// does not delete the files on disk + // shared functionality for removing references to a database from this program instance + // does not delete the files on disk void closeDatabase( const char *cl, const string& path = dbpath ); - /* remove database from the databases map */ - inline void eraseDatabase( const char *ns, const string& path=dbpath ) { - string key = makeDbKeyStr( ns, path ); - databases.erase( key ); - } - inline bool clientIsEmpty() { return !cc().database()->namespaceIndex.allocated(); } @@ -183,6 +223,7 @@ namespace mongo { extern TicketHolder connTicketHolder; + } // namespace mongo //#include "dbinfo.h" diff --git a/db/dbcommands.cpp b/db/dbcommands.cpp index 278565ea151..3464ac7b010 100644 --- a/db/dbcommands.cpp +++ b/db/dbcommands.cpp @@ -720,10 +720,11 @@ namespace mongo { seen.insert( i->c_str() ); } - - for ( map::iterator i = databases.begin(); i != databases.end(); i++ ){ - string name = i->first; - name = name.substr( 0 , name.find( ":" ) ); + + set allShortNames; + dbHolder.getAllShortNames( allShortNames ); + for ( set::iterator i = allShortNames.begin(); i != allShortNames.end(); i++ ){ + string name = *i; if ( seen.count( name ) ) continue; @@ -748,18 +749,7 @@ namespace mongo { virtual bool slaveOk() { return false; } CmdCloseAllDatabases() : Command( "closeAllDatabases" ) {} bool run(const char *ns, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { - set< string > dbs; - for ( map::iterator i = databases.begin(); i != databases.end(); i++ ) { - string name = i->first; - name = name.substr( 0 , name.find( ":" ) ); - dbs.insert( name ); - } - for( set< string >::iterator i = dbs.begin(); i != dbs.end(); ++i ) { - setClient( i->c_str() ); - closeDatabase( i->c_str() ); - } - - return true; + return dbHolder.closeAll( dbpath , result ); } } cmdCloseAllDatabases; diff --git a/db/dbwebserver.cpp b/db/dbwebserver.cpp index 837cb7caf89..1ba0a6c36e8 100644 --- a/db/dbwebserver.cpp +++ b/db/dbwebserver.cpp @@ -117,9 +117,9 @@ namespace mongo { public: // caller locks void doLockedStuff(stringstream& ss) { - ss << "# databases: " << databases.size() << '\n'; + ss << "# databases: " << dbHolder.size() << '\n'; if ( cc().database() ) { - ss << "curclient: " << cc().database()->name; + ss << "curclient: " << cc().database()->name; // TODO: isn't this useless? ss << '\n'; } ss << bold(ClientCursor::byLocSize()>10000) << "Cursors byLoc.size(): " << ClientCursor::byLocSize() << bold() << '\n'; diff --git a/db/instance.cpp b/db/instance.cpp index f9b8619dba1..ec3191e24fa 100644 --- a/db/instance.cpp +++ b/db/instance.cpp @@ -444,7 +444,7 @@ namespace mongo { NamespaceDetailsTransient::clearForPrefix( prefix.c_str() ); - eraseDatabase( cl, path ); + dbHolder.erase( cl, path ); delete database; // closes files cc().clearns(); } diff --git a/db/pdfile.cpp b/db/pdfile.cpp index 45e3b274571..0d6b000a204 100644 --- a/db/pdfile.cpp +++ b/db/pdfile.cpp @@ -46,7 +46,7 @@ namespace mongo { string dbpath = "/data/db/"; DataFileMgr theDataFileMgr; - map databases; + DatabaseHolder dbHolder; int MAGIC = 0x1000; // int curOp = -2; @@ -1812,4 +1812,31 @@ namespace mongo { NamespaceDetails* nsdetails_notinline(const char *ns) { return nsdetails(ns); } + bool DatabaseHolder::closeAll( const string& path , BSONObjBuilder& result ){ + log(2) << "DatabaseHolder::closeAll path:" << path << endl; + dbMutex.assertWriteLocked(); + + map& m = _paths[path]; + _size -= m.size(); + + set< string > dbs; + for ( map::iterator i = m.begin(); i != m.end(); i++ ) { + dbs.insert( i->first ); + } + + BSONObjBuilder bb( result.subarrayStart( "dbs" ) ); + int n = 0; + for( set< string >::iterator i = dbs.begin(); i != dbs.end(); ++i ) { + string name = *i; + log(2) << "DatabaseHolder::closeAll path:" << path << " name:" << name << endl; + setClient( name.c_str() , path ); + closeDatabase( name.c_str() , path ); + bb.append( bb.numStr( n++ ).c_str() , name ); + } + bb.done(); + + return true; + } + + } // namespace mongo diff --git a/jstests/dbadmin.js b/jstests/dbadmin.js new file mode 100644 index 00000000000..4bf0caed4f6 --- /dev/null +++ b/jstests/dbadmin.js @@ -0,0 +1,14 @@ + +t = db.dbadmin; +t.save( { x : 1 } ); + +before = db._adminCommand( "serverStatus" ); +db._adminCommand( "closeAllDatabases" ); +after = db._adminCommand( "serverStatus" ); +assert( before.mem.mapped > after.mem.mapped , "closeAllDatabases does something" ); + +t.save( { x : 1 } ); + +res = db._adminCommand( "listDatabases" ); +assert( res.databases.length > 0 , "listDatabases 1" ); +printjson( res );