void waitForWriteConcern(OperationContext* opCtx, const CommandInvocation* invocation, const repl::OpTime& lastOpBeforeRun, BSONObjBuilder& commandResponseBuilder) const override { auto lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); // Ensures that if we tried to do a write, we wait for write concern, even if that write was // a noop. if ((lastOpAfterRun == lastOpBeforeRun) && GlobalLockAcquisitionTracker::get(opCtx).getGlobalExclusiveLockTaken()) { repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx); lastOpAfterRun = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); } WriteConcernResult res; auto waitForWCStatus = mongo::waitForWriteConcern(opCtx, lastOpAfterRun, opCtx->getWriteConcern(), &res); CommandHelpers::appendCommandWCStatus(commandResponseBuilder, waitForWCStatus, res); // SERVER-22421: This code is to ensure error response backwards compatibility with the // user management commands. This can be removed in 3.6. if (!waitForWCStatus.isOK() && invocation->definition()->isUserManagementCommand()) { BSONObj temp = commandResponseBuilder.asTempObj().copy(); commandResponseBuilder.resetToEmpty(); CommandHelpers::appendCommandStatus(commandResponseBuilder, waitForWCStatus); commandResponseBuilder.appendElementsUnique(temp); } }
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) { auto command = globalCommandRegistry()->findCommand(request.getCommandName()); invariant(command); BSONObjBuilder out; try { bool ok = command->publicRun(opCtx, request, out); appendCommandStatus(out, ok); } catch (const StaleConfigException&) { // These exceptions are intended to be handled at a higher level. throw; } catch (const DBException& ex) { out.resetToEmpty(); appendCommandStatus(out, ex.toStatus()); } return out.obj(); }
void Command::execCommandClient(OperationContext* txn, Command* c, int queryOptions, const char* ns, BSONObj& cmdObj, BSONObjBuilder& result) { std::string dbname = nsToDatabase(ns); if (cmdObj.getBoolField("help")) { stringstream help; help << "help for: " << c->getName() << " "; c->help(help); result.append("help", help.str()); appendCommandStatus(result, true, ""); return; } Status status = checkAuthorization(c, txn, dbname, cmdObj); if (!status.isOK()) { appendCommandStatus(result, status); return; } c->_commandsExecuted.increment(); if (c->shouldAffectCommandCounter()) { globalOpCounters.gotCommand(); } StatusWith<WriteConcernOptions> wcResult = WriteConcernOptions::extractWCFromCommand(cmdObj, dbname); if (!wcResult.isOK()) { appendCommandStatus(result, wcResult.getStatus()); return; } bool supportsWriteConcern = c->supportsWriteConcern(cmdObj); if (!supportsWriteConcern && !wcResult.getValue().usedDefault) { // This command doesn't do writes so it should not be passed a writeConcern. // If we did not use the default writeConcern, one was provided when it shouldn't have // been by the user. appendCommandStatus( result, Status(ErrorCodes::InvalidOptions, "Command does not support writeConcern")); return; } // attach tracking rpc::TrackingMetadata trackingMetadata; trackingMetadata.initWithOperName(c->getName()); rpc::TrackingMetadata::get(txn) = trackingMetadata; std::string errmsg; bool ok = false; try { if (!supportsWriteConcern) { ok = c->run(txn, dbname, cmdObj, queryOptions, errmsg, result); } else { // Change the write concern while running the command. const auto oldWC = txn->getWriteConcern(); ON_BLOCK_EXIT([&] { txn->setWriteConcern(oldWC); }); txn->setWriteConcern(wcResult.getValue()); ok = c->run(txn, dbname, cmdObj, queryOptions, errmsg, result); } } catch (const DBException& e) { result.resetToEmpty(); const int code = e.getCode(); // Codes for StaleConfigException if (code == ErrorCodes::RecvStaleConfig || code == ErrorCodes::SendStaleConfig) { throw; } errmsg = e.what(); result.append("code", code); } if (!ok) { c->_commandsFailed.increment(); } appendCommandStatus(result, ok, errmsg); }