diff --git a/db/client.cpp b/db/client.cpp index 8341dedbb68..b19daaa5bcc 100644 --- a/db/client.cpp +++ b/db/client.cpp @@ -34,11 +34,13 @@ namespace mongo { Client::Client(const char *desc) : _curOp(new CurOp()), - _database(0), _ns("")/*, _nsstr("")*/ - ,_shutdown(false), + _context(0), + //_database(0), _ns("")/*, _nsstr("")*/ + _shutdown(false), _desc(desc), - _god(0) - { + _god(0), + _prevDB( 0 ) + { ai = new AuthenticationInfo(); boostlock bl(clientsMutex); clients.insert(this); @@ -49,9 +51,11 @@ namespace mongo { delete ai; ai = 0; _god = 0; - if ( !_shutdown ) { + + if ( _context ) + cout << "ERROR: Client::~Client _context should be NULL" << endl; + if ( !_shutdown ) cout << "ERROR: Client::shutdown not called!" << endl; - } } bool Client::shutdown(){ @@ -69,7 +73,7 @@ namespace mongo { for ( list::iterator i = _tempCollections.begin(); i!=_tempCollections.end(); i++ ){ string ns = *i; dblock l; - setClient( ns.c_str() ); + Client::Context ctx( ns ); if ( ! nsdetails( ns.c_str() ) ) continue; try { @@ -91,9 +95,31 @@ namespace mongo { AtomicUInt CurOp::_nextOpNum; Client::Context::Context( string ns , Database * db ) - : _client( currentClient.get() ) { + : _client( currentClient.get() ) , _oldContext( _client->_context ) , + _path( dbpath ) , _lock(0) , _justCreated(false) { assert( db && db->isOk() ); - _client->setns( ns.c_str() , db ); + _ns = ns; + _db = db; + _client->_context = this; + } + + void Client::Context::_finishInit(){ + dbMutex.assertAtLeastReadLocked(); + + _db = dbHolder.get( _ns , _path ); + if ( _db ){ + _justCreated = false; + } + else { + // we need to be in a write lock since we're going to create the DB object + if ( _lock ) + _lock->releaseAndWriteLock(); + assertInWriteLock(); + + _db = dbHolder.getOrCreate( _ns , _path , _justCreated ); + } + + _client->_context = this; } } diff --git a/db/client.h b/db/client.h index 99092caca45..43d4d1ef674 100644 --- a/db/client.h +++ b/db/client.h @@ -39,9 +39,6 @@ namespace mongo { extern boost::thread_specific_ptr currentClient; - bool setClient(const char *ns, const string& path=dbpath, mongolock *lock = 0); - - class Client : boost::noncopyable { public: static boost::mutex clientsMutex; @@ -57,30 +54,40 @@ namespace mongo { /* Set database we want to use, then, restores when we finish (are out of scope) Note this is also helpful if an exception happens as the state if fixed up. */ - class Context { + class Context : boost::noncopyable{ Client * _client; - Database * _olddb; - string _oldns; + Context * _oldContext; + + string _path; + mongolock * _lock; + bool _justCreated; + + string _ns; + Database * _db; + + /** + * at this point _client, _oldContext and _ns have to be set + * _db should not have been touched + * this will set _db and create if needed + * will also set _client->_context to this + */ + void _finishInit(); + + public: - Context(const char *ns) - : _client( currentClient.get() ) { - _olddb = _client->_database; - _oldns = _client->_ns; - setClient(ns); - } - Context(string ns) - : _client( currentClient.get() ){ - _olddb = _client->_database; - _oldns = _client->_ns; - setClient(ns.c_str()); + Context(const string& ns, string path=dbpath, mongolock * lock = 0 ) + : _client( currentClient.get() ) , _oldContext( _client->_context ) , + _path( path ) , _lock( lock ) , + _ns( ns ){ + _finishInit(); } /* this version saves the context but doesn't yet set the new one: */ Context() - : _client( currentClient.get() ) { - _olddb = _client->database(); - _oldns = _client->ns(); - + : _client( currentClient.get() ) , _oldContext( _client->_context ), + _path( dbpath ) , _lock(0) , _justCreated(false){ + _client->_context = this; + clear(); } /** @@ -91,37 +98,80 @@ namespace mongo { ~Context() { DEV assert( _client == currentClient.get() ); - _client->setns( _oldns.c_str(), _olddb ); + _client->_context = _oldContext; // note: _oldContext may be null + _client->_prevDB = _db; + } + + Database* db() const { + return _db; } - }; + const char * ns() const { + return _ns.c_str(); + } + + bool justCreated() const { + return _justCreated; + } + bool equals( const string& ns , const string& path=dbpath ) const { + return _ns == ns && _path == path; + } + + bool inDB( const string& db , const string& path=dbpath ) const { + if ( _path != path ) + return false; + + if ( db == _ns ) + return true; + + string::size_type idx = _ns.find( db ); + if ( idx != 0 ) + return false; + + return _ns[db.size()] == '.'; + } + + void clear(){ + _ns = ""; + _db = 0; + } + + /** + * call before unlocking, so clear any non-thread safe state + */ + void unlocked(){ + _db = 0; + } + + /** + * call after going back into the lock, will re-establish non-thread safe stuff + */ + void relocked(){ + _finishInit(); + } + }; + private: CurOp * const _curOp; - Database *_database; - Namespace _ns; - //NamespaceString _nsstr; + Context * _context; bool _shutdown; list _tempCollections; const char *_desc; bool _god; + + Database * _prevDB; public: AuthenticationInfo *ai; Top top; CurOp* curop() { return _curOp; } - Database* database() { - return _database; - } - const char *ns() { return _ns.buf; } - - void setns(const char *ns, Database *db) { - _database = db; - _ns = ns; - //_nsstr = ns; - } - void clearns() { setns("", 0); } - + + Context* getContext(){ return _context; } + Database* database() { return _context ? _context->db() : 0; } + const char *ns() { return _context->ns(); } + Database* prevDatabase(){ return _prevDB; } + Client(const char *desc); ~Client(); @@ -185,7 +235,8 @@ namespace mongo { /* this is defensive; as we were unlocked for a moment above, the Database object we reference could have been deleted: */ - cc().clearns(); + assert( ! cc().getContext() ); + //cc().clearns(); } } diff --git a/db/cloner.cpp b/db/cloner.cpp index 33b14142bea..bf819bbf7ac 100644 --- a/db/cloner.cpp +++ b/db/cloner.cpp @@ -462,7 +462,7 @@ namespace mongo { /* replication note: we must logOp() not the command, but the cloned data -- if the slave were to clone it would get a different point-in-time and not match. */ - setClient( collection.c_str() ); + Client::Context ctx( collection ); log() << "cloneCollection. db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << " logSizeMb: " << logSizeMb << ( copyIndexes ? "" : ", not copying indexes" ) << endl; @@ -506,7 +506,7 @@ namespace mongo { /* replication note: we must logOp() not the command, but the cloned data -- if the slave were to clone it would get a different point-in-time and not match. */ - setClient( collection.c_str() ); + Client::Context ctx(collection); log() << "startCloneCollection. db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << endl; @@ -562,7 +562,7 @@ namespace mongo { cursorId = cursorIdToken._numberLong(); } - setClient( collection.c_str() ); + Client::Context ctx( collection ); log() << "finishCloneCollection. db:" << ns << " collection:" << collection << " from: " << fromhost << " query: " << query << endl; @@ -601,9 +601,8 @@ namespace mongo { errmsg = "parms missing - {copydb: 1, fromhost: , fromdb: , todb: }"; return false; } - setClient(todb.c_str()); + Client::Context ctx(todb); bool res = cloneFrom(fromhost.c_str(), errmsg, fromdb, /*logForReplication=*/!fromRepl, /*slaveok*/false, /*replauth*/false, /*snapshot*/true); - cc().clearns(); return res; } } cmdcopydb; @@ -631,16 +630,19 @@ namespace mongo { return false; } - setClient( source.c_str() ); - NamespaceDetails *nsd = nsdetails( source.c_str() ); - uassert( 10026 , "source namespace does not exist", nsd ); - bool capped = nsd->capped; + bool capped = false; long long size = 0; - if ( capped ) - for( DiskLoc i = nsd->firstExtent; !i.isNull(); i = i.ext()->xnext ) - size += i.ext()->length; + { + Client::Context ctx( source ); + NamespaceDetails *nsd = nsdetails( source.c_str() ); + uassert( 10026 , "source namespace does not exist", nsd ); + capped = nsd->capped; + if ( capped ) + for( DiskLoc i = nsd->firstExtent; !i.isNull(); i = i.ext()->xnext ) + size += i.ext()->length; + } - setClient( target.c_str() ); + Client::Context ctx( target ); if ( nsdetails( target.c_str() ) ){ uassert( 10027 , "target namespace exists", cmdObj["dropTarget"].trueValue() ); @@ -715,8 +717,10 @@ namespace mongo { theDataFileMgr.insert( targetIndexes.c_str(), n ); } - setClient( source.c_str() ); - dropCollection( source, errmsg, result ); + { + Client::Context ctx( source ); + dropCollection( source, errmsg, result ); + } return true; } } cmdrenamecollection; diff --git a/db/db.cpp b/db/db.cpp index a472f074a3e..1e63bc90e53 100644 --- a/db/db.cpp +++ b/db/db.cpp @@ -69,7 +69,10 @@ namespace mongo { const char *ourgetns() { Client *c = currentClient.get(); - return c ? c->ns() : ""; + if ( ! c ) + return ""; + Client::Context* cc = c->getContext(); + return cc ? cc->ns() : ""; } struct MyStartupTests { @@ -82,7 +85,7 @@ namespace mongo { void testTheDb() { OpDebug debug; - setClient("sys.unittest.pdfile"); + Client::Context ctx("sys.unittest.pdfile"); /* this is not validly formatted, if you query this namespace bad things will happen */ theDataFileMgr.insert("sys.unittest.pdfile", (void *) "hello worldx", 13); @@ -101,8 +104,6 @@ namespace mongo { c->advance(); } out() << endl; - - cc().clearns(); } MessagingPort *connGrab = 0; @@ -346,7 +347,7 @@ namespace mongo { for ( vector< string >::iterator i = dbNames.begin(); i != dbNames.end(); ++i ) { string dbName = *i; log(1) << "\t" << dbName << endl; - assert( !setClient( dbName.c_str() ) ); + Client::Context ctx( dbName ); MongoDataFile *p = cc().database()->getFile( 0 ); MDFHeader *h = p->getHeader(); if ( !h->currentVersion() || forceRepair ) { @@ -394,8 +395,9 @@ namespace mongo { boost::filesystem::remove_all( *i ); } } - + void clearTmpCollections() { + Client::GodScope gs; vector< string > toDelete; DBDirectClient cli; auto_ptr< DBClientCursor > c = cli.query( "local.system.namespaces", Query( fromjson( "{name:/^local.temp./}" ) ) ); @@ -408,7 +410,7 @@ namespace mongo { cli.dropCollection( *i ); } } - + /** * does background async flushes of mmapped files */ diff --git a/db/db.h b/db/db.h index e55e8091d34..d0e9642ef3b 100644 --- a/db/db.h +++ b/db/db.h @@ -49,7 +49,7 @@ namespace mongo { public: DatabaseHolder() : _size(0){ } - + Database * get( const string& ns , const string& path ){ dbMutex.assertAtLeastReadLocked(); map& m = _paths[path]; @@ -71,6 +71,26 @@ namespace mongo { d = db; } + Database* getOrCreate( const string& ns , const string& path , bool& justCreated ){ + dbMutex.assertWriteLocked(); + map& 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; + db = new Database( dbname.c_str() , justCreated , path ); + return db; + } + + + + void erase( const string& ns , const string& path ){ dbMutex.assertWriteLocked(); map& m = _paths[path]; @@ -113,81 +133,42 @@ namespace mongo { extern DatabaseHolder dbHolder; - /* returns true if the database ("database") did not exist, and it was created on this call - path - datafiles directory, if not the default, so we can differentiate between db's of the same - name in different places (for example temp ones on repair). - */ - inline bool setClient(const char *ns, const string& path , mongolock *lock ) { - if( logLevel > 5 ) - log() << "setClient: " << ns << endl; - - dbMutex.assertAtLeastReadLocked(); - - Client& c = cc(); - c.top.clientStart( ns ); - - Database * db = dbHolder.get( ns , path ); - if ( db ){ - c.setns(ns, db ); - return false; - } - - if( lock ) - lock->releaseAndWriteLock(); - - assertInWriteLock(); - - char cl[256]; - nsToDatabase(ns, cl); - bool justCreated; - Database *newdb = new Database(cl, justCreated, path); - dbHolder.put(ns,path,newdb); - c.setns(ns, newdb); - - newdb->finishInit(); - - return justCreated; - } - // 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 ); - + struct dbtemprelease { - string clientname; - string clientpath; - int locktype; + Client::Context * _context; + int _locktype; + dbtemprelease() { - Client& client = cc(); - Database *database = client.database(); - if ( database ) { - clientname = database->name; - clientpath = database->path; - } - client.top.clientStop(); - locktype = dbMutex.getState(); - assert( locktype ); - if ( locktype > 0 ) { - massert( 10298 , "can't temprelease nested write lock", locktype == 1); + _context = cc().getContext(); + _locktype = dbMutex.getState(); + assert( _locktype ); + + if ( _locktype > 0 ) { + massert( 10298 , "can't temprelease nested write lock", _locktype == 1); + if ( _context ) _context->unlocked(); dbMutex.unlock(); } else { - massert( 10299 , "can't temprelease nested read lock", locktype == -1); + massert( 10299 , "can't temprelease nested read lock", _locktype == -1); + if ( _context ) _context->unlocked(); dbMutex.unlock_shared(); } + } ~dbtemprelease() { - if ( locktype > 0 ) + if ( _locktype > 0 ) dbMutex.lock(); else dbMutex.lock_shared(); - if ( clientname.empty() ) - cc().setns("", 0); - else - setClient(clientname.c_str(), clientpath.c_str()); + + if ( _context ) _context->relocked(); } }; + /** only does a temp release if we're not nested and have a lock */ diff --git a/db/dbcommands.cpp b/db/dbcommands.cpp index 2b78b62e6ee..8ebc53eebe7 100644 --- a/db/dbcommands.cpp +++ b/db/dbcommands.cpp @@ -761,8 +761,8 @@ namespace mongo { b.append( "name", i->c_str() ); boost::intmax_t size = dbSize( i->c_str() ); b.append( "sizeOnDisk", (double) size ); - setClient( i->c_str() ); - b.appendBool( "empty", cc().database()->isEmpty() ); + Client::Context ctx( *i ); + b.appendBool( "empty", ctx.db()->isEmpty() ); totalSize += size; dbInfos.push_back( b.obj() ); @@ -780,8 +780,8 @@ namespace mongo { BSONObjBuilder b; b << "name" << name << "sizeOnDisk" << double( 1 ); - setClient( name.c_str() ); - b.appendBool( "empty", cc().database()->isEmpty() ); + Client::Context ctx( name ); + b.appendBool( "empty", ctx.db()->isEmpty() ); dbInfos.push_back( b.obj() ); } @@ -927,9 +927,10 @@ namespace mongo { BSONObj max = jsobj.getObjectField( "max" ); BSONObj keyPattern = jsobj.getObjectField( "keyPattern" ); + Client::Context ctx( ns ); + auto_ptr< Cursor > c; if ( min.isEmpty() && max.isEmpty() ) { - setClient( ns ); c = theDataFileMgr.findAll( ns ); } else if ( min.isEmpty() || max.isEmpty() ) { errmsg = "only one of min or max specified"; @@ -1039,7 +1040,6 @@ namespace mongo { string fromNs = string( realDbName ) + "." + from; string toNs = string( realDbName ) + "." + to; - massert( 10300 , "source collection " + fromNs + " does not exist", !setClient( fromNs.c_str() ) ); NamespaceDetails *nsd = nsdetails( fromNs.c_str() ); massert( 10301 , "source collection " + fromNs + " does not exist", nsd ); long long excessSize = nsd->datasize - size * 2; @@ -1061,7 +1061,7 @@ namespace mongo { } DBDirectClient client; - setClient( toNs.c_str() ); + Client::Context ctx( toNs ); BSONObjBuilder spec; spec.appendBool( "capped", true ); spec.append( "size", double( size ) ); diff --git a/db/instance.cpp b/db/instance.cpp index e02c51cd60c..dca9caa3221 100644 --- a/db/instance.cpp +++ b/db/instance.cpp @@ -176,6 +176,8 @@ namespace mongo { QueryResult* msgdata; Client& c = cc(); + + Client::Context ctx( q.ns, dbpath, &lock ); try { if (q.fields.get() && q.fields->errmsg) @@ -192,7 +194,6 @@ namespace mongo { } } - setClient( q.ns, dbpath, &lock ); c.top.setRead(); c.curop()->setNS(q.ns); msgdata = runQuery(m, q, op ).release(); @@ -290,7 +291,6 @@ namespace mongo { } Client& c = cc(); - c.clearns(); auto_ptr nestedOp; CurOp* currentOpP = c.curop(); @@ -414,7 +414,7 @@ namespace mongo { ss << ' ' << ms << "ms"; mongo::log() << ss.str() << endl; } - Database *database = c.database(); + Database *database = c.prevDatabase(); if ( database && database->profile >= 1 ) { if ( database->profile >= 2 || ms >= cmdLine.slowMS ) { // performance profiling is on @@ -422,10 +422,8 @@ namespace mongo { out() << "warning: not profiling because recursive lock" << endl; } else { - string old_ns = c.ns(); - Database * old_db = c.database(); lk.releaseAndWriteLock(); - Client::Context c( old_ns , old_db ); + Client::Context c( "" , database ); profile(ss.str().c_str(), ms); } } @@ -452,20 +450,18 @@ namespace mongo { path - db directory */ void closeDatabase( const char *db, const string& path ) { - Database *database = cc().database(); - assert( database ); + assertInWriteLock(); + + Client::Context * ctx = cc().getContext(); + assert( ctx ); + assert( ctx->inDB( db , path ) ); + Database *database = ctx->db(); assert( database->name == db ); if( BackgroundOperation::inProgForDb(db) ) { log() << "warning: bg op in prog during close db? " << db << endl; } - /* - if ( string("local") != cl ) { - DBInfo i(cl); - i.dbDropped(); - }*/ - /* important: kill all open cursors on the database */ string prefix(db); prefix += '.'; @@ -475,7 +471,7 @@ namespace mongo { dbHolder.erase( db, path ); delete database; // closes files - cc().clearns(); + ctx->clear(); } void receivedUpdate(Message& m, CurOp& op) { @@ -483,9 +479,6 @@ namespace mongo { const char *ns = d.getns(); assert(*ns); uassert( 10054 , "not master", isMasterNs( ns ) ); - setClient(ns); - Client& client = cc(); - client.top.setWrite(); op.debug().str << ns << ' '; int flags = d.pullInt(); BSONObj query = d.nextJsObj(); @@ -502,9 +495,13 @@ namespace mongo { string s = query.toString(); /* todo: we shouldn't do all this ss stuff when we don't need it, it will slow us down. */ op.debug().str << " query: " << s; - CurOp& currentOp = *client.curop(); - currentOp.setQuery(query); + op.setQuery(query); } + + Client::Context ctx( ns ); + Client& client = cc(); + client.top.setWrite(); + UpdateResult res = updateObjects(ns, toupdate, query, upsert, multi, true, op.debug() ); /* TODO FIX: recordUpdate should take a long int for parm #2 */ recordUpdate( res.existing , (int) res.num ); // for getlasterror @@ -515,7 +512,7 @@ namespace mongo { const char *ns = d.getns(); assert(*ns); uassert( 10056 , "not master", isMasterNs( ns ) ); - setClient(ns); + Client::Context ctx(ns); Client& client = cc(); client.top.setWrite(); int flags = d.pullInt(); @@ -540,7 +537,7 @@ namespace mongo { const char *ns = d.getns(); StringBuilder& ss = curop.debug().str; ss << ns; - setClient(ns); + Client::Context ctx(ns); cc().top.setRead(); int ntoreturn = d.pullInt(); long long cursorid = d.pullInt64(); @@ -572,7 +569,7 @@ namespace mongo { const char *ns = d.getns(); assert(*ns); uassert( 10058 , "not master", isMasterNs( ns ) ); - setClient(ns); + Client::Context ctx(ns); cc().top.setWrite(); op.debug().str << ns; @@ -618,7 +615,6 @@ namespace mongo { } bool DBDirectClient::call( Message &toSend, Message &response, bool assertOk ) { - SavedContext c; if ( lastError._get() ) lastError.startRequest( toSend, lastError._get() ); DbResponse dbResponse; @@ -629,7 +625,6 @@ namespace mongo { } void DBDirectClient::say( Message &toSend ) { - SavedContext c; if ( lastError._get() ) lastError.startRequest( toSend, lastError._get() ); DbResponse dbResponse; @@ -647,8 +642,6 @@ namespace mongo { } - DBDirectClient::AlwaysAuthorized DBDirectClient::SavedContext::always; - DBClientBase * createDirectClient(){ return new DBDirectClient(); } diff --git a/db/instance.h b/db/instance.h index ce99f53f9db..2bf016d044c 100644 --- a/db/instance.h +++ b/db/instance.h @@ -124,53 +124,6 @@ namespace mongo { // don't need to piggy back when connected locally return say( toSend ); } - class AlwaysAuthorized : public AuthenticationInfo { - virtual bool _isAuthorized( const char *dbname, int level ) { - return true; - } - }; - - /* TODO: this looks bad that auth is set to always. is that really always safe? */ - class SavedContext { - public: - SavedContext() { - _save = dbMutex.atLeastReadLocked(); - - Client *c = currentClient.get(); - oldAuth = c->ai; - // careful, don't want to free this: - c->ai = &always; - - /* it only makes sense to manipulate a pointer - c->database() - if locked. - thus the _saved flag. - */ - if( _save ) { - if ( c->database() ) { - dbMutex.assertAtLeastReadLocked(); - _oldName = c->database()->name; - } - } - } - ~SavedContext() { - Client *c = currentClient.get(); - c->ai = oldAuth; - if( _save ) { - if ( !_oldName.empty() ) { - dbMutex.assertAtLeastReadLocked(); - setClient( _oldName.c_str() ); - } - } - else { - // defensive - cc().clearns(); - } - } - private: - bool _save; - static AlwaysAuthorized always; - AuthenticationInfo *oldAuth; - string _oldName; - }; }; extern int lockFile; diff --git a/db/introspect.cpp b/db/introspect.cpp index 9cb477d1db5..a041d489ffa 100644 --- a/db/introspect.cpp +++ b/db/introspect.cpp @@ -26,8 +26,7 @@ namespace mongo { - void profile(const char *str, - int millis) + void profile( const char *str, int millis) { BSONObjBuilder b; b.appendDate("ts", jsTime()); diff --git a/db/namespace.cpp b/db/namespace.cpp index c3584d8fc1e..fc8cc7e0dd0 100644 --- a/db/namespace.cpp +++ b/db/namespace.cpp @@ -644,7 +644,7 @@ namespace mongo { stringstream spec; // 128MB spec << "{size:" << logSizeMb * 1024 * 1024 << ",capped:true,autoIndexId:false}"; - setClient( _cll_ns.c_str() ); + Client::Context ct( _cll_ns ); string err; massert( 10347 , "Could not create log ns", userCreateNS( _cll_ns.c_str(), fromjson( spec.str() ), err, false ) ); NamespaceDetails *d = nsdetails( _cll_ns.c_str() ); @@ -670,7 +670,7 @@ namespace mongo { assertInWriteLock(); if ( !_cll_enabled ) return; - setClient( _cll_ns.c_str() ); + Client::Context ctx( _cll_ns ); dropNS( _cll_ns ); } diff --git a/db/pdfile.cpp b/db/pdfile.cpp index 5971185ba7a..0e9cd0e5c00 100644 --- a/db/pdfile.cpp +++ b/db/pdfile.cpp @@ -1654,11 +1654,16 @@ namespace mongo { "backup" : "$tmp" ); BOOST_CHECK_EXCEPTION( boost::filesystem::create_directory( reservedPath ) ); string reservedPathString = reservedPath.native_directory_string(); - assert( setClient( dbName, reservedPathString.c_str() ) ); - - bool res = cloneFrom(localhost.c_str(), errmsg, dbName, - /*logForReplication=*/false, /*slaveok*/false, /*replauth*/false, /*snapshot*/false); - closeDatabase( dbName, reservedPathString.c_str() ); + + bool res; + { // clone to temp location, which effectively does repair + Client::Context ctx( dbName, reservedPathString ); + assert( ctx.justCreated() ); + + res = cloneFrom(localhost.c_str(), errmsg, dbName, + /*logForReplication=*/false, /*slaveok*/false, /*replauth*/false, /*snapshot*/false); + closeDatabase( dbName, reservedPathString.c_str() ); + } if ( !res ) { problem() << "clone failed for " << dbName << " with error: " << errmsg << endl; @@ -1667,7 +1672,7 @@ namespace mongo { return false; } - assert( !setClient( dbName ) ); + Client::Context ctx( dbName ); closeDatabase( dbName ); if ( backupOriginalFiles ) { @@ -1738,7 +1743,7 @@ namespace mongo { 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 ); + Client::Context ctx( name , path ); if( !force && BackgroundOperation::inProgForDb(name.c_str()) ) log() << "WARNING: can't close database " << name << "because a bg job is in progress - try killOp command" << endl; else diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 499417a0f1c..214d99977a0 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -558,7 +558,7 @@ namespace mongo { return 0; } - setClient( ns ); + Client::Context ctx( ns ); IndexDetails *id = 0; NamespaceDetails *d = nsdetails( ns ); if ( !d ) { diff --git a/db/repl.cpp b/db/repl.cpp index a54ce50e58b..f6f0b40e46c 100644 --- a/db/repl.cpp +++ b/db/repl.cpp @@ -541,12 +541,13 @@ namespace mongo { BSONObj o = jsobj(); log( 1 ) << "Saving repl source: " << o << endl; - OpDebug debug; - setClient("local.sources"); - UpdateResult res = updateObjects("local.sources", o, pattern, true/*upsert for pair feature*/, false,false,debug); - assert( ! res.mod ); - assert( res.num == 1 ); - cc().clearns(); + { + OpDebug debug; + Client::Context ctx("local.sources"); + UpdateResult res = updateObjects("local.sources", o, pattern, true/*upsert for pair feature*/, false,false,debug); + assert( ! res.mod ); + assert( res.num == 1 ); + } if ( replacing ) { /* if we were in "replace" mode, we now have synced up with the replacement, @@ -578,13 +579,13 @@ namespace mongo { and cursor in effect. */ void ReplSource::loadAll(SourceVector &v) { + Client::Context ctx("local.sources"); SourceVector old = v; v.clear(); bool gotPairWith = false; if ( !cmdLine.source.empty() ) { - setClient("local.sources"); // --source specified. // check that no items are in sources other than that // add if missing @@ -626,7 +627,6 @@ namespace mongo { if ( replPair ) { const string &remote = replPair->remote; - setClient( "local.sources" ); // --pairwith host specified. // check that no items are in sources other than that // add if missing @@ -652,7 +652,6 @@ namespace mongo { } } - setClient("local.sources"); auto_ptr c = findTableScan("local.sources", BSONObj()); while ( c->ok() ) { ReplSource tmp(c->current()); @@ -668,7 +667,6 @@ namespace mongo { addSourceToList(v, tmp, c->current(), old); c->advance(); } - cc().clearns(); if ( !gotPairWith && replPair ) { /* add the --pairwith server */ @@ -732,7 +730,7 @@ namespace mongo { string ReplSource::resyncDrop( const char *db, const char *requester ) { log() << "resync: dropping database " << db << endl; string dummyns = string( db ) + "."; - setClient(dummyns.c_str()); + Client::Context ctx(dummyns); assert( cc().database()->name == db ); dropDatabase(dummyns.c_str()); return dummyns; @@ -741,7 +739,7 @@ namespace mongo { /* grab initial copy of a database from the master */ bool ReplSource::resync(string db) { string dummyNs = resyncDrop( db.c_str(), "internal" ); - setClient( dummyNs.c_str() ); + Client::Context ctx( dummyNs ); { log() << "resync: cloning database " << db << endl; ReplInfo r("resync: cloning a database"); @@ -864,29 +862,21 @@ namespace mongo { throw SyncException(); } - bool justCreated; - try { - justCreated = setClient(ns); - } catch ( AssertionException& ) { - problem() << "skipping bad(?) op in oplog, setClient() failed, ns: '" << ns << "'\n"; - addDbNextPass.erase(clientName); - return; - } + Client::Context ctx( ns ); - bool empty = cc().database()->isEmpty(); + bool empty = ctx.db()->isEmpty(); bool incompleteClone = incompleteCloneDbs.count( clientName ) != 0; - log( 6 ) << "ns: " << ns << ", justCreated: " << justCreated << ", empty: " << empty << ", incompleteClone: " << incompleteClone << endl; - - // always apply admin command command - // this is a bit hacky -- the semantics of replication/commands aren't well specified - if ( strcmp( clientName, "admin" ) == 0 && *op.getStringField( "op" ) == 'c' ) { - applyOperation( op ); - cc().clearns(); - return; - } + log( 6 ) << "ns: " << ns << ", justCreated: " << ctx.justCreated() << ", empty: " << empty << ", incompleteClone: " << incompleteClone << endl; - if ( justCreated || empty || incompleteClone ) { + // always apply admin command command + // this is a bit hacky -- the semantics of replication/commands aren't well specified + if ( strcmp( clientName, "admin" ) == 0 && *op.getStringField( "op" ) == 'c' ) { + applyOperation( op ); + return; + } + + if ( ctx.justCreated() || empty || incompleteClone ) { // we must add to incomplete list now that setClient has been called incompleteCloneDbs.insert( clientName ); if ( nClonedThisPass ) { @@ -901,9 +891,9 @@ namespace mongo { log() << "An earlier initial clone of '" << clientName << "' did not complete, now resyncing." << endl; } save(); - setClient( ns ); + Client::Context ctx(ns); nClonedThisPass++; - resync(cc().database()->name); + resync(ctx.db()->name); addDbNextPass.erase(clientName); incompleteCloneDbs.erase( clientName ); } @@ -927,7 +917,6 @@ namespace mongo { } addDbNextPass.erase( clientName ); } - cc().clearns(); } BSONObj ReplSource::idForOp( const BSONObj &op, bool &mod ) { @@ -990,7 +979,7 @@ namespace mongo { } OpTime ReplSource::nextLastSavedLocalTs() const { - setClient( "local.oplog.$main" ); + Client::Context ctx( "local.oplog.$main" ); auto_ptr< Cursor > c = findTableScan( "local.oplog.$main", BSON( "$natural" << -1 ) ); if ( c->ok() ) return OpTime( c->current().getField( "ts" ).date() ); @@ -1015,7 +1004,7 @@ namespace mongo { } bool ReplSource::updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock ) { - setClient( "local.oplog.$main" ); + Client::Context ctx( "local.oplog.$main" ); auto_ptr< Cursor > localLog = findTableScan( "local.oplog.$main", BSON( "$natural" << -1 ) ); OpTime newTail; for( ; localLog->ok(); localLog->advance() ) { @@ -1385,7 +1374,7 @@ namespace mongo { // cached copies of these...so don't rename them NamespaceDetails *localOplogMainDetails = 0; - Database *localOplogClient = 0; + Database *localOplogDB = 0; void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONObj *patt, bool *b) { if ( master ) { @@ -1447,14 +1436,14 @@ namespace mongo { Record *r; if ( strncmp( logNS, "local.", 6 ) == 0 ) { // For now, assume this is olog main if ( localOplogMainDetails == 0 ) { - setClient("local."); - localOplogClient = cc().database(); + Client::Context ctx("local."); + localOplogDB = ctx.db(); localOplogMainDetails = nsdetails(logNS); } - cc().setns("", localOplogClient); // database = localOplogClient; + Client::Context ctx( "" , localOplogDB ); r = theDataFileMgr.fast_oplog_insert(localOplogMainDetails, logNS, len); } else { - setClient( logNS ); + Client::Context ctx( logNS ); assert( nsdetails( logNS ) ); r = theDataFileMgr.fast_oplog_insert( nsdetails( logNS ), logNS, len); } @@ -1640,7 +1629,7 @@ namespace mongo { dblock lk; const char * ns = "local.oplog.$main"; - setClient(ns); + Client::Context ctx(ns); if ( nsdetails( ns ) ) return; @@ -1673,7 +1662,6 @@ namespace mongo { BSONObj o = b.done(); userCreateNS(ns, o, err, false); logOp( "n", "dummy", BSONObj() ); - cc().clearns(); } void startReplication() { diff --git a/db/security.h b/db/security.h index c5019d934ec..b5578f141d0 100644 --- a/db/security.h +++ b/db/security.h @@ -67,6 +67,7 @@ namespace mongo { if( authWriteOnly && ( 1 >= level ) ) return true; if( m["admin"].level >= level ) return true; if( m["local"].level >= level ) return true; + if( cc().isGod() ) return true; if( isLocalHost ) { readlock l(""); Client::Context c("admin.system.users"); diff --git a/dbtests/btreetests.cpp b/dbtests/btreetests.cpp index 5a0b15dfca1..3c9dc8defb5 100644 --- a/dbtests/btreetests.cpp +++ b/dbtests/btreetests.cpp @@ -28,14 +28,14 @@ namespace BtreeTests { class Base { public: - Base() { + Base() : + _context( ns() ) { + { bool f = false; assert( f = true ); massert( 10402 , "assert is misdefined", f); } - - setClient( ns() ); BSONObjBuilder builder; builder.append( "ns", ns() ); builder.append( "name", "testIndex" ); @@ -100,6 +100,7 @@ namespace BtreeTests { } private: dblock lk_; + Client::Context _context; IndexDetails idx_; }; diff --git a/dbtests/cursortests.cpp b/dbtests/cursortests.cpp index 28a4ba45703..adb7881c495 100644 --- a/dbtests/cursortests.cpp +++ b/dbtests/cursortests.cpp @@ -42,7 +42,7 @@ namespace CursorTests { BoundList b; b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 1 ), BSON( "" << 2 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 4 ), BSON( "" << 6 ) ) ); - setClient( ns ); + Client::Context ctx( ns ); BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, 1 ); ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() ); double expected[] = { 1, 2, 4, 5, 6 }; @@ -72,7 +72,7 @@ namespace CursorTests { b.push_back( pair< BSONObj, BSONObj >( BSON( "" << -50 ), BSON( "" << 2 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 40 ), BSON( "" << 60 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 109 ), BSON( "" << 200 ) ) ); - setClient( ns ); + Client::Context ctx( ns ); BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, 1 ); ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() ); double expected[] = { 0, 1, 2, 109 }; @@ -99,7 +99,7 @@ namespace CursorTests { BoundList b; b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 6 ), BSON( "" << 4 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 2 ), BSON( "" << 1 ) ) ); - setClient( ns ); + Client::Context ctx( ns ); BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, -1 ); ASSERT_EQUALS( "BtreeCursor a_1 reverse multi", c.toString() ); double expected[] = { 6, 5, 4, 2, 1 }; diff --git a/dbtests/namespacetests.cpp b/dbtests/namespacetests.cpp index c820ca67f06..205c5d24223 100644 --- a/dbtests/namespacetests.cpp +++ b/dbtests/namespacetests.cpp @@ -30,9 +30,9 @@ namespace NamespaceTests { namespace IndexDetailsTests { class Base { dblock lk; + Client::Context _context; public: - Base() { - setClient( ns() ); + Base() : _context(ns()){ } virtual ~Base() { if ( id_.info.isNull() ) @@ -571,9 +571,11 @@ namespace NamespaceTests { namespace NamespaceDetailsTests { class Base { + const char *ns_; dblock lk; + Client::Context _context; public: - Base( const char *ns = "unittests.NamespaceDetailsTests" ) : ns_( ns ) {} + Base( const char *ns = "unittests.NamespaceDetailsTests" ) : ns_( ns ) , _context( ns ) {} virtual ~Base() { if ( !nsd() ) return; @@ -585,7 +587,6 @@ namespace NamespaceTests { protected: void create() { dblock lk; - setClient( ns() ); string err; ASSERT( userCreateNS( ns(), fromjson( spec() ), err, false ) ); } @@ -627,8 +628,6 @@ namespace NamespaceTests { b.append( "a", as ); return b.obj(); } - private: - const char *ns_; }; class Create : public Base { diff --git a/dbtests/pdfiletests.cpp b/dbtests/pdfiletests.cpp index 17659c0ffeb..fbacf8b1e25 100644 --- a/dbtests/pdfiletests.cpp +++ b/dbtests/pdfiletests.cpp @@ -31,8 +31,7 @@ namespace PdfileTests { class Base { public: - Base() { - setClient( ns() ); + Base() : _context( ns() ){ } virtual ~Base() { if ( !nsd() ) @@ -99,6 +98,7 @@ namespace PdfileTests { } private: dblock lk_; + Client::Context _context; }; class Empty : public Base { @@ -269,8 +269,7 @@ namespace PdfileTests { namespace Insert { class Base { public: - Base() { - setClient( ns() ); + Base() : _context( ns() ){ } virtual ~Base() { if ( !nsd() ) @@ -287,6 +286,7 @@ namespace PdfileTests { } private: dblock lk_; + Client::Context _context; }; class UpdateDate : public Base { diff --git a/dbtests/perf/perftest.cpp b/dbtests/perf/perftest.cpp index 6fe9d6aacfb..02b2fadd766 100644 --- a/dbtests/perf/perftest.cpp +++ b/dbtests/perf/perftest.cpp @@ -623,7 +623,7 @@ namespace Plan { client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i ); } lk_.reset( new dblock ); - setClient( ns_.c_str() ); + Client::Context ctx( ns_ ); hint_ = BSON( "hint" << BSON( "a" << 1 ) ); hintElt_ = hint_.firstElement(); } @@ -646,9 +646,9 @@ namespace Plan { client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i ); } lk_.reset( new dblock ); - setClient( ns_.c_str() ); } void run() { + Client::Context ctx( ns_ ); for( int i = 0; i < 10000; ++i ) QueryPlanSet s( ns_.c_str(), BSONObj(), BSON( "a" << 1 ) ); } @@ -665,9 +665,9 @@ namespace Plan { client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i ); } lk_.reset( new dblock ); - setClient( ns_.c_str() ); } void run() { + Client::Context ctx( ns_.c_str() ); for( int i = 0; i < 10000; ++i ) QueryPlanSet s( ns_.c_str(), BSON( "a" << 1 ), BSONObj() ); } diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp index c9465f3479e..c7d09cdc1d9 100644 --- a/dbtests/queryoptimizertests.cpp +++ b/dbtests/queryoptimizertests.cpp @@ -316,8 +316,7 @@ namespace QueryOptimizerTests { namespace QueryPlanTests { class Base { public: - Base() : indexNum_( 0 ) { - setClient( ns() ); + Base() : _ctx( ns() ) , indexNum_( 0 ) { string err; userCreateNS( ns(), BSONObj(), err, false ); } @@ -357,6 +356,7 @@ namespace QueryOptimizerTests { } private: dblock lk_; + Client::Context _ctx; int indexNum_; static DBDirectClient client_; }; @@ -595,8 +595,7 @@ namespace QueryOptimizerTests { namespace QueryPlanSetTests { class Base { public: - Base() { - setClient( ns() ); + Base() : _context( ns() ){ string err; userCreateNS( ns(), BSONObj(), err, false ); } @@ -625,6 +624,7 @@ namespace QueryOptimizerTests { static NamespaceDetails *nsd() { return nsdetails( ns() ); } private: dblock lk_; + Client::Context _context; }; class NoIndexes : public Base { diff --git a/dbtests/querytests.cpp b/dbtests/querytests.cpp index 4681bf0adfd..2a25b206e9a 100644 --- a/dbtests/querytests.cpp +++ b/dbtests/querytests.cpp @@ -31,10 +31,9 @@ namespace QueryTests { class Base { dblock lk; + Client::Context _context; public: - Base() { - dblock lk; - setClient( ns() ); + Base() : _context( ns() ){ addIndex( fromjson( "{\"a\":1}" ) ); } ~Base() { @@ -608,7 +607,7 @@ namespace QueryTests { public: void run() { dblock lk; - setClient( "unittests.DirectLocking" ); + Client::Context ctx( "unittests.DirectLocking" ); client().remove( "a.b", BSONObj() ); ASSERT_EQUALS( "unittests", cc().database()->name ); } @@ -725,7 +724,7 @@ namespace QueryTests { string err; writelock lk(""); - setClient( "unittests" ); + Client::Context ctx( "unittests" ); ASSERT( userCreateNS( ns() , fromjson( "{ capped : true , size : 2000 }" ) , err , false ) ); for ( int i=0; i<100; i++ ){ @@ -770,7 +769,7 @@ namespace QueryTests { void run(){ writelock lk(""); - setClient( "unittests" ); + Client::Context ctx( "unittests" ); for ( int i=0; i<50; i++ ){ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) ); @@ -836,7 +835,7 @@ namespace QueryTests { void run(){ writelock lk(""); - setClient( "unittests" ); + Client::Context ctx( "unittests" ); for ( int i=0; i<1000; i++ ){ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) ); @@ -860,7 +859,7 @@ namespace QueryTests { void run(){ writelock lk(""); - setClient( "unittests" ); + Client::Context ctx( "unittests" ); for ( int i=0; i<1000; i++ ){ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) ); diff --git a/dbtests/repltests.cpp b/dbtests/repltests.cpp index 244c3079e79..3015a877cde 100644 --- a/dbtests/repltests.cpp +++ b/dbtests/repltests.cpp @@ -37,12 +37,12 @@ namespace ReplTests { } class Base { + dblock lk; + Client::Context _context; public: - Base() { + Base() : _context( ns() ){ master = true; createOplog(); - dblock lk; - setClient( ns() ); ensureHaveIdIndex( ns() ); } ~Base() { @@ -88,7 +88,7 @@ namespace ReplTests { int count() const { int count = 0; dblock lk; - setClient( ns() ); + Client::Context ctx( ns() ); auto_ptr< Cursor > c = theDataFileMgr.findAll( ns() ); for(; c->ok(); c->advance(), ++count ) { // cout << "obj: " << c->current().toString() << endl; @@ -97,7 +97,7 @@ namespace ReplTests { } static int opCount() { dblock lk; - setClient( cllNS() ); + Client::Context ctx( cllNS() ); int count = 0; for( auto_ptr< Cursor > c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() ) ++count; @@ -111,17 +111,21 @@ namespace ReplTests { } }; dblock lk; - setClient( cllNS() ); vector< BSONObj > ops; - for( auto_ptr< Cursor > c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() ) - ops.push_back( c->current() ); - setClient( ns() ); - for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i ) - Applier::apply( *i ); + { + Client::Context ctx( cllNS() ); + for( auto_ptr< Cursor > c = theDataFileMgr.findAll( cllNS() ); c->ok(); c->advance() ) + ops.push_back( c->current() ); + } + { + Client::Context ctx( ns() ); + for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i ) + Applier::apply( *i ); + } } static void printAll( const char *ns ) { dblock lk; - setClient( ns ); + Client::Context ctx( ns ); auto_ptr< Cursor > c = theDataFileMgr.findAll( ns ); vector< DiskLoc > toDelete; out() << "all for " << ns << endl; @@ -132,7 +136,7 @@ namespace ReplTests { // These deletes don't get logged. static void deleteAll( const char *ns ) { dblock lk; - setClient( ns ); + Client::Context ctx( ns ); auto_ptr< Cursor > c = theDataFileMgr.findAll( ns ); vector< DiskLoc > toDelete; for(; c->ok(); c->advance() ) { @@ -144,7 +148,7 @@ namespace ReplTests { } static void insert( const BSONObj &o, bool god = false ) { dblock lk; - setClient( ns() ); + Client::Context ctx( ns() ); theDataFileMgr.insert( ns(), o.objdata(), o.objsize(), god ); } static BSONObj wid( const char *json ) { @@ -908,7 +912,7 @@ namespace ReplTests { class DbIdsTest { public: void run() { - setClient( "unittests.repltest.DbIdsTest" ); + Client::Context ctx( "unittests.repltest.DbIdsTest" ); s_.reset( new DbIds( "local.temp.DbIdsTest" ) ); s_->reset(); @@ -983,7 +987,7 @@ namespace ReplTests { class IdTrackerTest { public: void run() { - setClient( "unittests.repltests.IdTrackerTest" ); + Client::Context ctx( "unittests.repltests.IdTrackerTest" ); ASSERT( s_.inMem() ); s_.reset( 4 * sizeof( BSONObj ) - 1 );