virtual void appendToResponse(BSONObjBuilder* result) const final { if (lockedForWriting()) { result->append("fsyncLock", true); result->append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } }
void inProgCmd( Message &m, DbResponse &dbresponse ) { BSONObjBuilder b; if (!cc().getAuthorizationManager()->checkAuthorization( AuthorizationManager::SERVER_RESOURCE_NAME, ActionType::inprog)) { b.append("err", "unauthorized"); } else { DbMessage d(m); QueryMessage q(d); bool all = q.query["$all"].trueValue(); vector<BSONObj> vals; { Client& me = cc(); scoped_lock bl(Client::clientsMutex); scoped_ptr<Matcher> m(new Matcher(q.query)); for( set<Client*>::iterator i = Client::clients.begin(); i != Client::clients.end(); i++ ) { Client *c = *i; verify( c ); CurOp* co = c->curop(); if ( c == &me && !co ) { continue; } verify( co ); if( all || co->displayInCurop() ) { BSONObj info = co->info(); if ( all || m->matches( info )) { vals.push_back( info ); } } } } b.append("inprog", vals); if( lockedForWriting() ) { b.append("fsyncLock", true); b.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } } replyToQuery(0, m, dbresponse, b.obj()); }
virtual void run() { Client::initThread( name().c_str() ); while ( ! inShutdown() ) { sleepsecs( 60 ); LOG(3) << "TTLMonitor thread awake" << endl; if ( lockedForWriting() ) { // note: this is not perfect as you can go into fsync+lock between // this and actually doing the delete later LOG(3) << " locked for writing" << endl; continue; } // if part of replSet but not in a readable state (e.g. during initial sync), skip. if ( theReplSet && !theReplSet->state().readable() ) continue; set<string> dbs; { Lock::DBRead lk( "local" ); dbHolder().getAllShortNames( dbs ); } ttlPasses.increment(); for ( set<string>::const_iterator i=dbs.begin(); i!=dbs.end(); ++i ) { string db = *i; try { doTTLForDB( db ); } catch ( DBException& e ) { error() << "error processing ttl for db: " << db << " " << e << endl; } } } }
bool run(OperationContext* txn, const std::string& db, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); const bool ownOpsOnly = cmdObj["$ownOps"].trueValue(); // Filter the output BSONObj filter; { BSONObjBuilder b; BSONObjIterator i(cmdObj); invariant(i.more()); i.next(); // skip {currentOp: 1} which is required to be the first element while (i.more()) { BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; } else if (str::equals("$ownOps", e.fieldName())) { continue; } b.append(e); } filter = b.obj(); } std::vector<BSONObj> inprogInfos; BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); for (ServiceContext::LockedClientsCursor cursor(txn->getClient()->getServiceContext()); Client* client = cursor.next();) { invariant(client); stdx::lock_guard<Client> lk(*client); if (ownOpsOnly && !AuthorizationSession::get(txn->getClient())->isCoauthorizedWithClient(client)) { continue; } const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { // Skip over inactive connections. if (!opCtx) continue; } BSONObjBuilder infoBuilder; // The client information client->reportState(infoBuilder); const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata(); if (clientMetadata) { auto appName = clientMetadata.get().getApplicationName(); if (!appName.empty()) { infoBuilder.append("appName", appName); } } // Operation context specific information infoBuilder.appendBool("active", static_cast<bool>(opCtx)); if (opCtx) { infoBuilder.append("opid", opCtx->getOpID()); if (opCtx->isKillPending()) { infoBuilder.append("killPending", true); } CurOp::get(opCtx)->reportState(&infoBuilder); // LockState Locker::LockerInfo lockerInfo; opCtx->lockState()->getLockerInfo(&lockerInfo); fillLockerInfo(lockerInfo, infoBuilder); } // If we want to include all results or if the filter is empty, then we can append // straight to the inprogBuilder, but otherwise we should run the filter Matcher // outside this loop so we don't lock the ServiceContext while matching - in some cases // this can cause deadlocks. if (includeAll || filter.isEmpty()) { inprogBuilder.append(infoBuilder.obj()); } else { inprogInfos.emplace_back(infoBuilder.obj()); } } if (!inprogInfos.empty()) { // We use ExtensionsCallbackReal here instead of ExtensionsCallbackNoop in order to // support the use case of having a $where filter with currentOp. However, since we // don't have a collection, we pass in a fake collection name (and this is okay, // because $where parsing only relies on the database part of the namespace). const NamespaceString fakeNS(db, "$dummyNamespaceForCurrop"); const Matcher matcher(filter, ExtensionsCallbackReal(txn, &fakeNS), nullptr); for (const auto& info : inprogInfos) { if (matcher.matches(info)) { inprogBuilder.append(info); } } } inprogBuilder.done(); if (lockedForWriting()) { result.append("fsyncLock", true); result.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } return true; }
// Returns false when request includes 'end' void assembleResponse( Message &m, DbResponse &dbresponse, const HostAndPort& remote ) { // before we lock... int op = m.operation(); bool isCommand = false; const char *ns = m.singleData()->_data + 4; if ( op == dbQuery ) { if( strstr(ns, ".$cmd") ) { isCommand = true; opwrite(m); if( strstr(ns, ".$cmd.sys.") ) { if( strstr(ns, "$cmd.sys.inprog") ) { inProgCmd(m, dbresponse); return; } if( strstr(ns, "$cmd.sys.killop") ) { killOp(m, dbresponse); return; } if( strstr(ns, "$cmd.sys.unlock") ) { unlockFsync(ns, m, dbresponse); return; } } } else { opread(m); } } else if( op == dbGetMore ) { opread(m); } else { opwrite(m); } globalOpCounters.gotOp( op , isCommand ); Client& c = cc(); c.getAuthorizationManager()->startRequest(); auto_ptr<CurOp> nestedOp; CurOp* currentOpP = c.curop(); if ( currentOpP->active() ) { nestedOp.reset( new CurOp( &c , currentOpP ) ); currentOpP = nestedOp.get(); } else { c.newTopLevelRequest(); } CurOp& currentOp = *currentOpP; currentOp.reset(remote,op); OpDebug& debug = currentOp.debug(); debug.op = op; long long logThreshold = cmdLine.slowMS; bool shouldLog = logLevel >= 1; if ( op == dbQuery ) { if ( handlePossibleShardedMessage( m , &dbresponse ) ) return; receivedQuery(c , dbresponse, m ); } else if ( op == dbGetMore ) { if ( ! receivedGetMore(dbresponse, m, currentOp) ) shouldLog = true; } else if ( op == dbMsg ) { // deprecated - replaced by commands char *p = m.singleData()->_data; int len = strlen(p); if ( len > 400 ) out() << curTimeMillis64() % 10000 << " long msg received, len:" << len << endl; Message *resp = new Message(); if ( strcmp( "end" , p ) == 0 ) resp->setData( opReply , "dbMsg end no longer supported" ); else resp->setData( opReply , "i am fine - dbMsg deprecated"); dbresponse.response = resp; dbresponse.responseTo = m.header()->id; } else { try { const NamespaceString nsString( ns ); // The following operations all require authorization. // dbInsert, dbUpdate and dbDelete can be easily pre-authorized, // here, but dbKillCursors cannot. if ( op == dbKillCursors ) { currentOp.ensureStarted(); logThreshold = 10; receivedKillCursors(m); } else if ( !nsString.isValid() ) { // Only killCursors doesn't care about namespaces uassert( 16257, str::stream() << "Invalid ns [" << ns << "]", false ); } else if ( op == dbInsert ) { receivedInsert(m, currentOp); } else if ( op == dbUpdate ) { receivedUpdate(m, currentOp); } else if ( op == dbDelete ) { receivedDelete(m, currentOp); } else { mongo::log() << " operation isn't supported: " << op << endl; currentOp.done(); shouldLog = true; } } catch ( UserException& ue ) { tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " << ue.toString() << endl; debug.exceptionInfo = ue.getInfo(); } catch ( AssertionException& e ) { tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " << e.toString() << endl; debug.exceptionInfo = e.getInfo(); shouldLog = true; } } currentOp.ensureStarted(); currentOp.done(); debug.executionTime = currentOp.totalTimeMillis(); logThreshold += currentOp.getExpectedLatencyMs(); if ( shouldLog || debug.executionTime > logThreshold ) { mongo::tlog() << debug.report( currentOp ) << endl; } if ( currentOp.shouldDBProfile( debug.executionTime ) ) { // performance profiling is on if ( Lock::isReadLocked() ) { LOG(1) << "note: not profiling because recursive read lock" << endl; } else if ( lockedForWriting() ) { LOG(1) << "note: not profiling because doing fsync+lock" << endl; } else { profile(c, op, currentOp); } } debug.recordStats(); debug.reset(); } /* assembleResponse() */
// Returns false when request includes 'end' void assembleResponse( Message &m, DbResponse &dbresponse, const HostAndPort& remote ) { // before we lock... int op = m.operation(); bool isCommand = false; const char *ns = m.singleData()->_data + 4; if ( op == dbQuery ) { if( strstr(ns, ".$cmd") ) { isCommand = true; opwrite(m); if( strstr(ns, ".$cmd.sys.") ) { if( strstr(ns, "$cmd.sys.inprog") ) { inProgCmd(m, dbresponse); return; } if( strstr(ns, "$cmd.sys.killop") ) { killOp(m, dbresponse); return; } if( strstr(ns, "$cmd.sys.unlock") ) { unlockFsync(ns, m, dbresponse); return; } } } else { opread(m); } } else if( op == dbGetMore ) { opread(m); } else { opwrite(m); } globalOpCounters.gotOp( op , isCommand ); Client& c = cc(); auto_ptr<CurOp> nestedOp; CurOp* currentOpP = c.curop(); if ( currentOpP->active() ) { nestedOp.reset( new CurOp( &c , currentOpP ) ); currentOpP = nestedOp.get(); } else { c.newTopLevelRequest(); } CurOp& currentOp = *currentOpP; currentOp.reset(remote,op); OpDebug& debug = currentOp.debug(); debug.op = op; int logThreshold = cmdLine.slowMS; bool log = logLevel >= 1; if ( op == dbQuery ) { if ( handlePossibleShardedMessage( m , &dbresponse ) ) return; receivedQuery(c , dbresponse, m ); } else if ( op == dbGetMore ) { if ( ! receivedGetMore(dbresponse, m, currentOp) ) log = true; } else if ( op == dbMsg ) { // deprecated - replaced by commands char *p = m.singleData()->_data; int len = strlen(p); if ( len > 400 ) out() << curTimeMillis64() % 10000 << " long msg received, len:" << len << endl; Message *resp = new Message(); if ( strcmp( "end" , p ) == 0 ) resp->setData( opReply , "dbMsg end no longer supported" ); else resp->setData( opReply , "i am fine - dbMsg deprecated"); dbresponse.response = resp; dbresponse.responseTo = m.header()->id; } else { try { // The following operations all require authorization. // dbInsert, dbUpdate and dbDelete can be easily pre-authorized, // here, but dbKillCursors cannot. if ( op == dbKillCursors ) { currentOp.ensureStarted(); logThreshold = 10; receivedKillCursors(m); } else if ( ! c.getAuthenticationInfo()->isAuthorized( nsToDatabase( m.singleData()->_data + 4 ) ) ) { uassert_nothrow("unauthorized"); } else if ( op == dbInsert ) { receivedInsert(m, currentOp); } else if ( op == dbUpdate ) { receivedUpdate(m, currentOp); } else if ( op == dbDelete ) { receivedDelete(m, currentOp); } else { mongo::log() << " operation isn't supported: " << op << endl; currentOp.done(); log = true; } } catch ( UserException& ue ) { tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " << ue.toString() << endl; debug.exceptionInfo = ue.getInfo(); } catch ( AssertionException& e ) { tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " << e.toString() << endl; debug.exceptionInfo = e.getInfo(); log = true; } } currentOp.ensureStarted(); currentOp.done(); debug.executionTime = currentOp.totalTimeMillis(); //DEV log = true; if ( log || debug.executionTime > logThreshold ) { if( logLevel < 3 && op == dbGetMore && strstr(ns, ".oplog.") && debug.executionTime < 4300 && !log ) { /* it's normal for getMore on the oplog to be slow because of use of awaitdata flag. */ } else { mongo::tlog() << debug << endl; } } if ( currentOp.shouldDBProfile( debug.executionTime ) ) { // performance profiling is on if ( Lock::isReadLocked() ) { mongo::log(1) << "note: not profiling because recursive read lock" << endl; } else if ( lockedForWriting() ) { mongo::log(1) << "note: not profiling because doing fsync+lock" << endl; } else { writelock lk; if ( dbHolder()._isLoaded( nsToDatabase( currentOp.getNS() ) , dbpath ) ) { Client::Context cx( currentOp.getNS(), dbpath, false ); profile(c , currentOp ); } else { mongo::log() << "note: not profiling because db went away - probably a close on: " << currentOp.getNS() << endl; } } } debug.reset(); } /* assembleResponse() */
bool run(OperationContext* txn, const std::string& db, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); const bool ownOpsOnly = cmdObj["$ownOps"].trueValue(); // Filter the output BSONObj filter; { BSONObjBuilder b; BSONObjIterator i(cmdObj); invariant(i.more()); i.next(); // skip {currentOp: 1} which is required to be the first element while (i.more()) { BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; } else if (str::equals("$ownOps", e.fieldName())) { continue; } b.append(e); } filter = b.obj(); } // We use ExtensionsCallbackReal here instead of ExtensionsCallbackNoop in order to support // the use case of having a $where filter with currentOp. However, since we don't have a // collection, we pass in a fake collection name (and this is okay, because $where parsing // only relies on the database part of the namespace). const NamespaceString fakeNS(db, "$cmd"); const CollatorInterface* collator = nullptr; const Matcher matcher(filter, ExtensionsCallbackReal(txn, &fakeNS), collator); BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); for (ServiceContext::LockedClientsCursor cursor(txn->getClient()->getServiceContext()); Client* client = cursor.next();) { invariant(client); stdx::lock_guard<Client> lk(*client); if (ownOpsOnly && !AuthorizationSession::get(txn->getClient())->isCoauthorizedWithClient(client)) { continue; } const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { // Skip over inactive connections. if (!opCtx) continue; } BSONObjBuilder infoBuilder; // The client information client->reportState(infoBuilder); // Operation context specific information infoBuilder.appendBool("active", static_cast<bool>(opCtx)); if (opCtx) { infoBuilder.append("opid", opCtx->getOpID()); if (opCtx->isKillPending()) { infoBuilder.append("killPending", true); } CurOp::get(opCtx)->reportState(&infoBuilder); // LockState Locker::LockerInfo lockerInfo; opCtx->lockState()->getLockerInfo(&lockerInfo); fillLockerInfo(lockerInfo, infoBuilder); } infoBuilder.done(); const BSONObj info = infoBuilder.obj(); if (includeAll || matcher.matches(info)) { inprogBuilder.append(info); } } inprogBuilder.done(); if (lockedForWriting()) { result.append("fsyncLock", true); result.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } return true; }
bool run(OperationContext* txn, const std::string& db, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); // Filter the output BSONObj filter; { BSONObjBuilder b; BSONObjIterator i(cmdObj); invariant(i.more()); i.next(); // skip {currentOp: 1} which is required to be the first element while (i.more()) { BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; } b.append(e); } filter = b.obj(); } const WhereCallbackReal whereCallback(txn, db); const Matcher matcher(filter, whereCallback); BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); for (ServiceContext::LockedClientsCursor cursor(txn->getClient()->getServiceContext()); Client* client = cursor.next();) { invariant(client); stdx::lock_guard<Client> lk(*client); const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { // Skip over inactive connections. if (!opCtx) continue; } BSONObjBuilder infoBuilder; // The client information client->reportState(infoBuilder); // Operation context specific information infoBuilder.appendBool("active", static_cast<bool>(opCtx)); if (opCtx) { infoBuilder.append("opid", opCtx->getOpID()); if (opCtx->isKillPending()) { infoBuilder.append("killPending", true); } CurOp::get(opCtx)->reportState(&infoBuilder); // LockState Locker::LockerInfo lockerInfo; opCtx->lockState()->getLockerInfo(&lockerInfo); fillLockerInfo(lockerInfo, infoBuilder); } infoBuilder.done(); const BSONObj info = infoBuilder.obj(); if (includeAll || matcher.matches(info)) { inprogBuilder.append(info); } } inprogBuilder.done(); if (lockedForWriting()) { result.append("fsyncLock", true); result.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } return true; }