summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2020-09-02 20:25:08 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-09-09 00:40:06 +0000
commit7b2913a64bc8812a9ab71ce27fe14bb2f517376f (patch)
tree5f849520a990cb13080ce28572fb8f3e5601044b
parentb11fe32330deab811a4cfcb190b111c4671148f1 (diff)
downloadmongo-7b2913a64bc8812a9ab71ce27fe14bb2f517376f.tar.gz
SERVER-50116 Forbid oplog writes when replication is enabled
-rw-r--r--jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js52
-rw-r--r--src/mongo/db/ops/insert.cpp5
2 files changed, 56 insertions, 1 deletions
diff --git a/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js b/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js
new file mode 100644
index 00000000000..1e77d01c561
--- /dev/null
+++ b/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js
@@ -0,0 +1,52 @@
+/**
+ * Tests that oplog writes are forbidden on replica set members. In standalone mode, it is permitted
+ * to insert oplog entries, which will be applied during replication recovery. This behavior is
+ * needed for point-in-time restores, which are supported on 4.2+.
+ * @tags: [
+ * requires_persistence,
+ * requires_replication,
+ * ]
+ */
+(function() {
+"use strict";
+
+const rst = new ReplSetTest({nodes: 1});
+rst.startSet();
+rst.initiateWithHighElectionTimeout();
+
+let conn = rst.getPrimary();
+assert.commandWorked(conn.getDB("test").coll.insert({_id: 0, a: 0}));
+
+let oplog = conn.getDB("local").oplog.rs;
+
+// Construct a valid oplog entry.
+const lastOplogEntry = oplog.find().sort({ts: -1}).limit(1).toArray()[0];
+const highestTS = lastOplogEntry.ts;
+const toInsert = Object.extend(lastOplogEntry, {
+ op: "u",
+ ns: "test.coll",
+ o: {$set: {a: 1}},
+ o2: {_id: 0},
+ ts: Timestamp(highestTS.getTime(), highestTS.getInc() + 1)
+});
+
+jsTestLog("Test that oplog writes are banned when replication is enabled.");
+assert.commandFailedWithCode(oplog.insert(toInsert), ErrorCodes.InvalidNamespace);
+
+jsTestLog("Restart the node in standalone mode.");
+rst.stop(0, undefined /*signal*/, undefined /*opts*/, {forRestart: true});
+conn = rst.start(0, {noReplSet: true, noCleanData: true});
+
+jsTestLog("Test that oplog writes are permitted in standalone mode.");
+assert.commandWorked(conn.getDB("local").oplog.rs.insert(toInsert));
+
+jsTestLog("Restart the node with replication enabled.");
+rst.stop(0, undefined /*signal*/, undefined /*opts*/, {forRestart: true});
+rst.start(0, {noCleanData: true});
+conn = rst.getPrimary();
+
+jsTestLog("The added oplog entry is applied as part of replication recovery.");
+assert.eq({_id: 0, a: 1}, conn.getDB("test").coll.findOne());
+
+rst.stopSet();
+}());
diff --git a/src/mongo/db/ops/insert.cpp b/src/mongo/db/ops/insert.cpp
index 383536913e1..fff03006b9f 100644
--- a/src/mongo/db/ops/insert.cpp
+++ b/src/mongo/db/ops/insert.cpp
@@ -34,6 +34,7 @@
#include "mongo/bson/bson_depth.h"
#include "mongo/db/commands/feature_compatibility_version_parser.h"
+#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/vector_clock_mutable.h"
#include "mongo/db/views/durable_view_catalog.h"
#include "mongo/util/str.h"
@@ -176,7 +177,9 @@ Status userAllowedWriteNS(const NamespaceString& ns) {
if (ns.isSystemDotProfile() ||
(ns.isSystemDotViews() && serverGlobalParams.featureCompatibility.isVersionInitialized() &&
serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo(
- ServerGlobalParams::FeatureCompatibility::Version::kVersion47))) {
+ ServerGlobalParams::FeatureCompatibility::Version::kVersion47)) ||
+ (ns.isOplog() &&
+ repl::ReplicationCoordinator::get(getGlobalServiceContext())->isReplEnabled())) {
return Status(ErrorCodes::InvalidNamespace, str::stream() << "cannot write to " << ns);
}
return userAllowedCreateNS(ns);