summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/oplog_note.cpp
diff options
context:
space:
mode:
authorMisha Tyulenev <misha@mongodb.com>2017-04-07 14:07:05 -0400
committerMisha Tyulenev <misha@mongodb.com>2017-04-07 14:59:03 -0400
commitfed0e2cc62a65067c7f2991554206a95d2619172 (patch)
tree3f8ce1477e1e97406a4aeaf9d2799c4520b3cac3 /src/mongo/db/commands/oplog_note.cpp
parent53934264e65978b631978e8fb60b8bdb1b643cab (diff)
downloadmongo-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.cpp96
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