From 2b198988a626df6f36c9e606b08b266c462cceb4 Mon Sep 17 00:00:00 2001 From: Bernard Gorman Date: Fri, 7 Jul 2017 04:47:07 +0100 Subject: SERVER-19318 Reimplement the currentOp command as a $currentOp aggregation --- src/mongo/db/commands/current_op.cpp | 172 ++++++----------------------------- 1 file changed, 26 insertions(+), 146 deletions(-) (limited to 'src/mongo/db/commands/current_op.cpp') diff --git a/src/mongo/db/commands/current_op.cpp b/src/mongo/db/commands/current_op.cpp index 510bf298cba..838f01c44f0 100644 --- a/src/mongo/db/commands/current_op.cpp +++ b/src/mongo/db/commands/current_op.cpp @@ -26,51 +26,28 @@ * it in the license file. */ -#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand - #include "mongo/platform/basic.h" -#include +#include "mongo/db/commands/current_op_common.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" -#include "mongo/db/commands.h" #include "mongo/db/commands/fsync.h" -#include "mongo/db/curop.h" -#include "mongo/db/dbmessage.h" -#include "mongo/db/jsobj.h" -#include "mongo/db/matcher/extensions_callback_real.h" -#include "mongo/db/matcher/matcher.h" -#include "mongo/db/namespace_string.h" -#include "mongo/db/operation_context.h" +#include "mongo/db/commands/run_aggregate.h" +#include "mongo/db/pipeline/document.h" #include "mongo/db/stats/fill_locker_info.h" -#include "mongo/rpc/metadata/client_metadata.h" -#include "mongo/rpc/metadata/client_metadata_ismaster.h" -#include "mongo/util/log.h" -#include "mongo/util/time_support.h" namespace mongo { -class CurrentOpCommand : public Command { -public: - CurrentOpCommand() : Command("currentOp") {} - - - virtual bool supportsWriteConcern(const BSONObj& cmd) const override { - return false; - } - - bool slaveOk() const final { - return true; - } +class CurrentOpCommand final : public CurrentOpCommandBase { + MONGO_DISALLOW_COPYING(CurrentOpCommand); - bool adminOnly() const final { - return true; - } +public: + CurrentOpCommand() = default; Status checkAuthForCommand(Client* client, - const std::string& dbname, + const std::string& dbName, const BSONObj& cmdObj) final { AuthorizationSession* authzSession = AuthorizationSession::get(client); if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), @@ -86,127 +63,30 @@ public: return Status(ErrorCodes::Unauthorized, "Unauthorized"); } - bool run(OperationContext* opCtx, - const std::string& db, - const BSONObj& cmdObj, - 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(); - const auto fieldName = e.fieldNameStringData(); - if (fieldName == "$all") { - continue; - } else if (fieldName == "$ownOps") { - continue; - } else if (Command::isGenericArgument(fieldName)) { - continue; - } - - b.append(e); - } - filter = b.obj(); - } + virtual StatusWith runAggregation( + OperationContext* opCtx, const AggregationRequest& request) const final { + auto aggCmdObj = request.serializeToCommandObj().toBson(); - std::vector inprogInfos; - BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); - - for (ServiceContext::LockedClientsCursor cursor(opCtx->getClient()->getServiceContext()); - Client* client = cursor.next();) { - invariant(client); - - stdx::lock_guard lk(*client); - - if (ownOpsOnly && - !AuthorizationSession::get(opCtx->getClient())->isCoauthorizedWithClient(client)) { - continue; - } - - const OperationContext* clientOpCtx = client->getOperationContext(); - - if (!includeAll) { - // Skip over inactive connections. - if (!clientOpCtx) - 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); - } - - auto clientMetadataDocument = clientMetadata.get().getDocument(); - infoBuilder.append("clientMetadata", clientMetadataDocument); - } - - // Operation context specific information - infoBuilder.appendBool("active", static_cast(clientOpCtx)); - infoBuilder.append("currentOpTime", Date_t::now().toString()); - if (clientOpCtx) { - infoBuilder.append("opid", clientOpCtx->getOpID()); - if (clientOpCtx->isKillPending()) { - infoBuilder.append("killPending", true); - } - - CurOp::get(clientOpCtx)->reportState(&infoBuilder); - - // LockState - Locker::LockerInfo lockerInfo; - clientOpCtx->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()); - } - } + BSONObjBuilder responseBuilder; + + auto status = runAggregate( + opCtx, request.getNamespaceString(), request, std::move(aggCmdObj), responseBuilder); - 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(opCtx, &fakeNS), nullptr); - - for (const auto& info : inprogInfos) { - if (matcher.matches(info)) { - inprogBuilder.append(info); - } - } + if (!status.isOK()) { + return status; } - inprogBuilder.done(); + appendCommandStatus(responseBuilder, Status::OK()); + + return CursorResponse::parseFromBSON(responseBuilder.obj()); + } + + 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"); + result->append("fsyncLock", true); + result->append("info", + "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } - - return true; } } currentOpCommand; -- cgit v1.2.1