diff options
-rw-r--r-- | src/mongo/db/client.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/client.h | 7 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.h | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/write_commands/batch_executor.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/curop.cpp | 71 | ||||
-rw-r--r-- | src/mongo/db/curop.h | 23 | ||||
-rw-r--r-- | src/mongo/db/curop_test.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/dbhelpers.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/operation_context_impl.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/query/find.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/service_context_d.cpp | 10 | ||||
-rw-r--r-- | src/mongo/dbtests/query_stage_update.cpp | 4 | ||||
-rw-r--r-- | src/mongo/dbtests/querytests.cpp | 2 |
15 files changed, 127 insertions, 72 deletions
diff --git a/src/mongo/db/client.cpp b/src/mongo/db/client.cpp index c408723d462..6fe85d249e2 100644 --- a/src/mongo/db/client.cpp +++ b/src/mongo/db/client.cpp @@ -111,8 +111,6 @@ namespace mongo { _connectionId(p ? p->connectionId() : 0), _inDirectClient(false), _txn(NULL) { - - _curOp = new CurOp( this ); } Client::~Client() { @@ -122,13 +120,6 @@ namespace mongo { boost::lock_guard<boost::mutex> clientLock(clientsMutex); clients.erase(this); } - - CurOp* last; - do { - last = _curOp; - delete _curOp; - // _curOp may have been reset to _curOp->_wrapped - } while (_curOp != last); } } @@ -167,9 +158,13 @@ namespace mongo { } string Client::clientAddress(bool includePort) const { - if( _curOp ) - return _curOp->getRemoteString(includePort); - return ""; + if (!hasRemote()) { + return ""; + } + if (includePort) { + return getRemote().toString(); + } + return getRemote().host(); } ClientBasic* ClientBasic::getCurrent() { diff --git a/src/mongo/db/client.h b/src/mongo/db/client.h index 5b11d057e99..b2a6dbf6950 100644 --- a/src/mongo/db/client.h +++ b/src/mongo/db/client.h @@ -49,7 +49,6 @@ namespace mongo { - class CurOp; class Collection; class AbstractMessagingPort; @@ -98,7 +97,6 @@ namespace mongo { bool shutdown(); std::string clientAddress(bool includePort = false) const; - CurOp* curop() const { return _curOp; } const std::string& desc() const { return _desc; } void reportState(BSONObjBuilder& builder); @@ -122,8 +120,6 @@ namespace mongo { bool isFromUserConnection() const { return _connectionId > 0; } private: - friend class CurOp; - Client(const std::string& desc, ServiceContext* serviceContext, AbstractMessagingPort *p = 0); @@ -146,9 +142,6 @@ namespace mongo { // If != NULL, then contains the currently active OperationContext OperationContext* _txn; - - // Changes, based on what operation is running. Some of this should be in OperationContext. - CurOp* _curOp; }; /** get the Client object for this thread. */ diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp index 26bea429d2a..9fc4c309aa5 100644 --- a/src/mongo/db/clientcursor.cpp +++ b/src/mongo/db/clientcursor.cpp @@ -44,7 +44,6 @@ #include "mongo/db/commands.h" #include "mongo/db/commands/server_status.h" #include "mongo/db/commands/server_status_metric.h" -#include "mongo/db/curop.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context_impl.h" #include "mongo/db/repl/repl_client_info.h" @@ -182,13 +181,13 @@ namespace mongo { _idleAgeMillis = millis; } - void ClientCursor::updateSlaveLocation(OperationContext* txn, CurOp& curop) { + void ClientCursor::updateSlaveLocation(OperationContext* txn) { if (_slaveReadTill.isNull()) return; verify(str::startsWith(_ns.c_str(), "local.oplog.")); - Client* c = curop.getClient(); + Client* c = txn->getClient(); verify(c); OID rid = repl::ReplClientInfo::forClient(c).getRemoteID(); if (!rid.isSet()) diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h index c5b8629d675..554fb0e7076 100644 --- a/src/mongo/db/clientcursor.h +++ b/src/mongo/db/clientcursor.h @@ -135,7 +135,7 @@ namespace mongo { // Replication-related stuff. TODO: Document and clean. // - void updateSlaveLocation(OperationContext* txn, CurOp& curop); + void updateSlaveLocation(OperationContext* txn); void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; } /** Just for testing. */ OpTime getSlaveReadTill() const { return _slaveReadTill; } diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index cd03c645c32..fceb7bd042c 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -922,7 +922,7 @@ namespace mongo { WriteErrorDetail** error ) { // BEGIN CURRENT OP - CurOp currentOp( _txn->getClient(), _txn->getClient()->curop() ); + CurOp currentOp(_txn->getClient()); beginCurrentOp( ¤tOp, _txn->getClient(), updateItem ); incOpStats( updateItem ); @@ -966,7 +966,7 @@ namespace mongo { // Removes are similar to updates, but page faults are handled externally // BEGIN CURRENT OP - CurOp currentOp( _txn->getClient(), _txn->getClient()->curop() ); + CurOp currentOp(_txn->getClient()); beginCurrentOp( ¤tOp, _txn->getClient(), removeItem ); incOpStats( removeItem ); @@ -1165,7 +1165,7 @@ namespace mongo { void WriteBatchExecutor::execOneInsert(ExecInsertsState* state, WriteErrorDetail** error) { BatchItemRef currInsertItem(state->request, state->currIndex); - CurOp currentOp( _txn->getClient(), _txn->getClient()->curop() ); + CurOp currentOp(_txn->getClient()); beginCurrentOp( ¤tOp, _txn->getClient(), currInsertItem ); incOpStats(currInsertItem); diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp index 439e0c20921..7d46b032486 100644 --- a/src/mongo/db/curop.cpp +++ b/src/mongo/db/curop.cpp @@ -33,12 +33,14 @@ #include "mongo/db/curop.h" #include "mongo/base/counter.h" +#include "mongo/base/disallow_copying.h" #include "mongo/db/client.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/catalog/database.h" #include "mongo/db/service_context.h" #include "mongo/db/json.h" #include "mongo/db/stats/top.h" +#include "mongo/util/exit.h" #include "mongo/util/fail_point_service.h" #include "mongo/util/log.h" @@ -46,6 +48,55 @@ namespace mongo { using std::string; + /** + * This type decorates a Client object with a stack of active CurOp objects. + * + * It encapsulates the nesting logic for curops attached to a Client, along with + * the notion that there is always a root CurOp attached to a Client. + * + * The stack itself is represented in the _parent pointers of the CurOp class. + */ + class CurOp::ClientCuropStack { + MONGO_DISALLOW_COPYING(ClientCuropStack); + public: + ClientCuropStack() : _base(this) {} + + /** + * Returns the top of the CurOp stack. + */ + CurOp* top() const { return _top; } + + /** + * Adds "curOp" to the top of the CurOp stack for a client. Called by CurOp's constructor. + */ + void push(CurOp* curOp) { + boost::lock_guard<boost::mutex> clientLock(Client::clientsMutex); + invariant(!curOp->_parent); + curOp->_parent = _top; + _top = curOp; + } + + /** + * Pops the top off the CurOp stack for a Client. Called by CurOp's destructor. + */ + CurOp* pop() { + boost::lock_guard<boost::mutex> clientLock(Client::clientsMutex); + invariant(_top); + CurOp* retval = _top; + _top = _top->_parent; + return retval; + } + + private: + // Top of the stack of CurOps for a Client. + CurOp* _top = nullptr; + + // The bottom-most CurOp for a client. + const CurOp _base; + }; + + const auto CurOp::_curopStack = Client::declareDecoration<CurOp::ClientCuropStack>(); + // Enabling the maxTimeAlwaysTimeOut fail point will cause any query or command run with a // valid non-zero max time to fail immediately. Any getmore operation on a cursor already // created with a valid non-zero max time will also fail immediately. @@ -64,12 +115,13 @@ namespace mongo { fromjson("{\"$msg\":\"query not recording (too large)\"}"); - CurOp::CurOp( Client * client , CurOp * wrapped ) : - _client(client), - _wrapped(wrapped) - { - if ( _wrapped ) - _client->_curOp = this; + CurOp* CurOp::get(const Client* client) { return _curopStack(client).top(); } + CurOp* CurOp::get(const Client& client) { return _curopStack(client).top(); } + + CurOp::CurOp(Client* client) : CurOp(&_curopStack(client)) {} + + CurOp::CurOp(ClientCuropStack* stack) : _stack(stack) { + _stack->push(this); _start = 0; _active = false; _reset(); @@ -130,11 +182,10 @@ namespace mongo { } CurOp::~CurOp() { - if ( _wrapped ) { - boost::lock_guard<boost::mutex> clientLock(Client::clientsMutex); - _client->_curOp = _wrapped; + if (!inShutdown()) { + // TODO(schwerin): See if there's a reason not to clean up during shutdown. + invariant(this == _stack->pop()); } - _client = 0; } void CurOp::setNS( StringData ns ) { diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h index 14bea54eab1..0228d2d6a7a 100644 --- a/src/mongo/db/curop.h +++ b/src/mongo/db/curop.h @@ -193,7 +193,10 @@ namespace mongo { */ class CurOp : boost::noncopyable { public: - CurOp( Client * client , CurOp * wrapped = 0 ); + static CurOp* get(const Client* client); + static CurOp* get(const Client& client); + + explicit CurOp(Client* client); ~CurOp(); bool haveQuery() const { return _query.have(); } @@ -278,11 +281,10 @@ namespace mongo { int elapsedSeconds() { return elapsedMillis() / 1000; } void setQuery(const BSONObj& query) { _query.set( query ); } - Client * getClient() const { return _client; } - + Command * getCommand() const { return _command; } void setCommand(Command* command) { _command = command; } - + void reportState(BSONObjBuilder* builder); // Fetches less information than "info()"; used to search for ops with certain criteria @@ -300,7 +302,7 @@ namespace mongo { int secondsBetween = 3); std::string getMessage() const { return _message.toString(); } ProgressMeter& getProgressMeter() { return _progressMeter; } - CurOp *parent() const { return _wrapped; } + CurOp *parent() const { return _parent; } void kill(); bool killPendingStrict() const { return _killPending.load(); } bool killPending() const { return _killPending.loadRelaxed(); } @@ -320,12 +322,17 @@ namespace mongo { void setNS( StringData ns ); private: - friend class Client; + class ClientCuropStack; + + static const Client::Decoration<ClientCuropStack> _curopStack; + + explicit CurOp(ClientCuropStack*); + void _reset(); static AtomicUInt32 _nextOpNum; - Client * _client; - CurOp * _wrapped; + ClientCuropStack* _stack; + CurOp* _parent = nullptr; Command * _command; long long _start; long long _end; diff --git a/src/mongo/db/curop_test.cpp b/src/mongo/db/curop_test.cpp index f8682c79481..85ad5d98973 100644 --- a/src/mongo/db/curop_test.cpp +++ b/src/mongo/db/curop_test.cpp @@ -31,7 +31,14 @@ #include <boost/thread/thread.hpp> #include "mongo/base/init.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/authz_manager_external_state_mock.h" +#include "mongo/db/client.h" #include "mongo/db/curop.h" +#include "mongo/db/service_context.h" +#include "mongo/db/service_context_noop.h" +#include "mongo/stdx/memory.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -67,12 +74,16 @@ namespace mongo { sleepmillis(10); } + setGlobalServiceContext(stdx::make_unique<ServiceContextNoop>()); + setGlobalAuthorizationManager( + new AuthorizationManager(new AuthzManagerExternalStateMock())); + Client::initThread("CurOpTestMain"); return Status::OK(); } // Long operation + short timeout => time should expire. TEST(TimeHasExpired, PosSimple) { - CurOp curOp(NULL); + CurOp curOp(&cc()); curOp.setMaxTimeMicros(intervalShort); curOp.ensureStarted(); sleepmicros(intervalLong); @@ -81,7 +92,7 @@ namespace mongo { // Short operation + long timeout => time should not expire. TEST(TimeHasExpired, NegSimple) { - CurOp curOp(NULL); + CurOp curOp(&cc()); curOp.setMaxTimeMicros(intervalLong); curOp.ensureStarted(); sleepmicros(intervalShort); diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index d74d74f493c..f9fb4f0d33a 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -270,7 +270,7 @@ namespace mongo { update(txn, context.db(), request, &debug); - txn->getClient()->curop()->done(); + CurOp::get(txn->getClient())->done(); } BSONObj Helpers::toKeyFormat( const BSONObj& o ) { diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index f5efc2938d4..1c49b54c3a1 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -217,7 +217,7 @@ namespace { DbMessage dbMessage(message); QueryMessage queryMessage(dbMessage); - CurOp* op = client.curop(); + CurOp* op = CurOp::get(client); std::unique_ptr<Message> response(new Message()); @@ -325,7 +325,7 @@ namespace { QueryMessage q(d); auto_ptr< Message > resp( new Message() ); - CurOp& op = *(c.curop()); + CurOp& op = *CurOp::get(c); try { Client* client = txn->getClient(); @@ -436,15 +436,13 @@ namespace { globalOpCounters.gotDelete(); break; } - + scoped_ptr<CurOp> nestedOp; - CurOp* currentOpP = c.curop(); - if ( currentOpP->active() ) { - nestedOp.reset( new CurOp( &c , currentOpP ) ); - currentOpP = nestedOp.get(); + if (CurOp::get(c)->active()) { + nestedOp.reset(new CurOp(&c)); } - CurOp& currentOp = *currentOpP; + CurOp& currentOp = *CurOp::get(c); currentOp.reset(remote,op); OpDebug& debug = currentOp.debug(); diff --git a/src/mongo/db/operation_context_impl.cpp b/src/mongo/db/operation_context_impl.cpp index 8c7a793ca86..9bb5cc6af60 100644 --- a/src/mongo/db/operation_context_impl.cpp +++ b/src/mongo/db/operation_context_impl.cpp @@ -126,7 +126,7 @@ namespace { } CurOp* OperationContextImpl::getCurOp() const { - return getClient()->curop(); + return CurOp::get(getClient()); } unsigned int OperationContextImpl::getOpID() const { @@ -162,7 +162,7 @@ namespace { } // Only target nested operations if requested. - if (!failPointInfo["allowNested"].trueValue() && c.curop()->parent() != NULL) { + if (!failPointInfo["allowNested"].trueValue() && CurOp::get(c)->parent() != NULL) { return false; } @@ -192,20 +192,21 @@ namespace { } Client* c = getClient(); - if (c->curop()->maxTimeHasExpired()) { - c->curop()->kill(); + if (CurOp::get(c)->maxTimeHasExpired()) { + CurOp::get(c)->kill(); return Status(ErrorCodes::ExceededTimeLimit, "operation exceeded time limit"); } MONGO_FAIL_POINT_BLOCK(checkForInterruptFail, scopedFailPoint) { if (opShouldFail(*c, scopedFailPoint.getData())) { - log() << "set pending kill on " << (c->curop()->parent() ? "nested" : "top-level") - << " op " << c->curop()->opNum() << ", for checkForInterruptFail"; - c->curop()->kill(); + log() << "set pending kill on " + << (CurOp::get(c)->parent() ? "nested" : "top-level") + << " op " << CurOp::get(c)->opNum() << ", for checkForInterruptFail"; + CurOp::get(c)->kill(); } } - if (c->curop()->killPending()) { + if (CurOp::get(c)->killPending()) { return Status(ErrorCodes::Interrupted, "operation was interrupted"); } diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index 9875b0c4a24..fb6218fc800 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -368,7 +368,7 @@ namespace mongo { txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. if (0 == pass) { - cc->updateSlaveLocation(txn, curop); + cc->updateSlaveLocation(txn); } if (cc->isAggCursor()) { diff --git a/src/mongo/db/service_context_d.cpp b/src/mongo/db/service_context_d.cpp index 4c3d30032df..50a9b660394 100644 --- a/src/mongo/db/service_context_d.cpp +++ b/src/mongo/db/service_context_d.cpp @@ -187,12 +187,12 @@ namespace mongo { bool ServiceContextMongoD::_killOperationsAssociatedWithClientAndOpId_inlock( Client* client, unsigned int opId) { - for( CurOp *k = client->curop(); k; k = k->parent() ) { + for( CurOp *k = CurOp::get(client); k; k = k->parent() ) { if ( k->opNum() != opId ) continue; k->kill(); - for( CurOp *l = client->curop(); l; l = l->parent() ) { + for( CurOp *l = CurOp::get(client); l; l = l->parent() ) { l->kill(); } @@ -237,15 +237,15 @@ namespace mongo { continue; } - if (client->curop()->opNum() == txn->getOpID()) { + if (CurOp::get(client)->opNum() == txn->getOpID()) { // Don't kill ourself. continue; } bool found = _killOperationsAssociatedWithClientAndOpId_inlock( - client, client->curop()->opNum()); + client, CurOp::get(client)->opNum()); if (!found) { - warning() << "Attempted to kill operation " << client->curop()->opNum() + warning() << "Attempted to kill operation " << CurOp::get(client)->opNum() << " but the opId changed"; } } diff --git a/src/mongo/dbtests/query_stage_update.cpp b/src/mongo/dbtests/query_stage_update.cpp index 752cb6d7dc5..0d118a64066 100644 --- a/src/mongo/dbtests/query_stage_update.cpp +++ b/src/mongo/dbtests/query_stage_update.cpp @@ -189,7 +189,7 @@ namespace QueryStageUpdate { { OldClientWriteContext ctx(&_txn, ns()); Client& c = cc(); - CurOp& curOp = *c.curop(); + CurOp& curOp = *CurOp::get(c); OpDebug* opDebug = &curOp.debug(); UpdateDriver driver( (UpdateDriver::Options()) ); Collection* collection = ctx.getCollection(); @@ -258,7 +258,7 @@ namespace QueryStageUpdate { ASSERT_EQUALS(10U, count(BSONObj())); Client& c = cc(); - CurOp& curOp = *c.curop(); + CurOp& curOp = *CurOp::get(c); OpDebug* opDebug = &curOp.debug(); UpdateDriver driver( (UpdateDriver::Options()) ); Database* db = ctx.db(); diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp index 4ce39171757..eb5cf7402f9 100644 --- a/src/mongo/dbtests/querytests.cpp +++ b/src/mongo/dbtests/querytests.cpp @@ -1509,7 +1509,7 @@ namespace QueryTests { DbMessage dbMessage( message ); QueryMessage queryMessage( dbMessage ); Message result; - string exhaust = runQuery(&_txn, queryMessage, NamespaceString(ns()), *cc().curop(), + string exhaust = runQuery(&_txn, queryMessage, NamespaceString(ns()), *CurOp::get(cc()), result); ASSERT( exhaust.size() ); ASSERT_EQUALS( string( ns() ), exhaust ); |