diff options
author | Misha Tyulenev <misha@mongodb.com> | 2017-04-07 14:07:05 -0400 |
---|---|---|
committer | Misha Tyulenev <misha@mongodb.com> | 2017-04-07 14:59:03 -0400 |
commit | fed0e2cc62a65067c7f2991554206a95d2619172 (patch) | |
tree | 3f8ce1477e1e97406a4aeaf9d2799c4520b3cac3 /src/mongo/db/commands/oplog_note.cpp | |
parent | 53934264e65978b631978e8fb60b8bdb1b643cab (diff) | |
download | mongo-fed0e2cc62a65067c7f2991554206a95d2619172.tar.gz |
SERVER-27772 Force noop write on primary when readConcern:afterClusterTime > clusterTime on secondaries
Diffstat (limited to 'src/mongo/db/commands/oplog_note.cpp')
-rw-r--r-- | src/mongo/db/commands/oplog_note.cpp | 96 |
1 files changed, 79 insertions, 17 deletions
diff --git a/src/mongo/db/commands/oplog_note.cpp b/src/mongo/db/commands/oplog_note.cpp index 0c59dca849a..fd6083a6a89 100644 --- a/src/mongo/db/commands/oplog_note.cpp +++ b/src/mongo/db/commands/oplog_note.cpp @@ -26,22 +26,59 @@ * then also delete it in the license file. */ -#include <string> +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand +#include "mongo/platform/basic.h" + +#include "mongo/db/commands.h" + +#include "mongo/base/init.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/resource_pattern.h" -#include "mongo/db/commands.h" #include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/db/concurrency/write_conflict_exception.h" +#include "mongo/db/curop.h" #include "mongo/db/jsobj.h" #include "mongo/db/op_observer.h" #include "mongo/db/operation_context.h" #include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/replication_coordinator_global.h" #include "mongo/db/service_context.h" +#include "mongo/util/log.h" namespace mongo { +namespace { +Status _performNoopWrite(OperationContext* opCtx, BSONObj msgObj, StringData note) { + repl::ReplicationCoordinator* const replCoord = repl::ReplicationCoordinator::get(opCtx); + // Use GlobalLock + lockMMAPV1Flush instead of DBLock to allow return when the lock is not + // available. It may happen when the primary steps down and a shared global lock is + // acquired. + Lock::GlobalLock lock(opCtx, MODE_IX, 1); + + if (!lock.isLocked()) { + LOG(1) << "Global lock is not available skipping noopWrite"; + return {ErrorCodes::LockFailed, "Global lock is not available"}; + } + opCtx->lockState()->lockMMAPV1Flush(); + + // Its a proxy for being a primary passing "local" will cause it to return true on secondary + if (!replCoord->canAcceptWritesForDatabase(opCtx, "admin")) { + return {ErrorCodes::NotMaster, "Not a primary"}; + } + + MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { + WriteUnitOfWork uow(opCtx); + opCtx->getClient()->getServiceContext()->getOpObserver()->onOpMessage(opCtx, msgObj); + uow.commit(); + } + MONGO_WRITE_CONFLICT_RETRY_LOOP_END(opCtx, note, repl::rsOplogName); + + return Status::OK(); +} +} // namespace using std::string; using std::stringstream; @@ -49,18 +86,23 @@ using std::stringstream; class AppendOplogNoteCmd : public Command { public: AppendOplogNoteCmd() : Command("appendOplogNote") {} + virtual bool slaveOk() const { return false; } + virtual bool adminOnly() const { return true; } + virtual bool supportsWriteConcern(const BSONObj& cmd) const override { return true; } + virtual void help(stringstream& help) const { help << "Adds a no-op entry to the oplog"; } + virtual Status checkAuthForCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -70,32 +112,52 @@ public: } return Status::OK(); } + virtual bool run(OperationContext* opCtx, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result) { - if (!repl::getGlobalReplicationCoordinator()->isReplEnabled()) { - return appendCommandStatus( - result, - Status(ErrorCodes::NoReplicationEnabled, - "Must have replication set up to run \"appendOplogNote\"")); - } BSONElement dataElement; - Status status = bsonExtractTypedField(cmdObj, "data", Object, &dataElement); - if (!status.isOK()) { - return appendCommandStatus(result, status); + auto dataStatus = bsonExtractTypedField(cmdObj, "data", Object, &dataElement); + if (!dataStatus.isOK()) { + return appendCommandStatus(result, dataStatus); } - Lock::GlobalWrite globalWrite(opCtx); + Timestamp clusterTime; + auto clusterTimeStatus = bsonExtractTimestampField(cmdObj, "clusterTime", &clusterTime); - WriteUnitOfWork wuow(opCtx); - getGlobalServiceContext()->getOpObserver()->onOpMessage(opCtx, dataElement.Obj()); - wuow.commit(); - return true; + auto replCoord = repl::ReplicationCoordinator::get(opCtx); + if (!replCoord->isReplEnabled()) { + return appendCommandStatus(result, + {ErrorCodes::NoReplicationEnabled, + "Must have replication set up to run \"appendOplogNote\""}); + } + + if (!clusterTimeStatus.isOK()) { + if (clusterTimeStatus == ErrorCodes::NoSuchKey) { // no need to use clusterTime + return appendCommandStatus( + result, _performNoopWrite(opCtx, dataElement.Obj(), "appendOpLogNote")); + } + return appendCommandStatus(result, clusterTimeStatus); + } + + auto lastAppliedOpTime = replCoord->getMyLastAppliedOpTime().getTimestamp(); + if (clusterTime > lastAppliedOpTime) { + return appendCommandStatus( + result, _performNoopWrite(opCtx, dataElement.Obj(), "appendOpLogNote")); + } else { + LOG(1) << "Not scheduling a noop write. Requested clusterTime" << clusterTime + << " is less or equal to the last primary OpTime: " << lastAppliedOpTime; + return appendCommandStatus(result, Status::OK()); + } } +}; -} appendOplogNoteCmd; +MONGO_INITIALIZER(RegisterAppendOpLogNoteCmd)(InitializerContext* context) { + new AppendOplogNoteCmd(); + return Status::OK(); +} } // namespace mongo |