summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuganthi Mani <suganthi.mani@mongodb.com>2018-12-06 17:55:25 -0500
committerSuganthi Mani <suganthi.mani@mongodb.com>2019-01-12 15:05:56 -0500
commit89d845e752312fce0e12be4cf8f9761e44adc004 (patch)
treed5542d992e91fd168cb6f3ccd35f63138e1e2cdc
parenta722798ac1732b18239b808b5363be0a1b16d166 (diff)
downloadmongo-89d845e752312fce0e12be4cf8f9761e44adc004.tar.gz
SERVER-37915 Updates serverStatus.opcountersRepl.command on secondaries.
And, atomic applyOps cmd increments serverStatus.opcounters<opType> for each individual operation on primary. (cherry picked from commit 214bf238fedc4e147e6473f5fc64428987added6)
-rw-r--r--jstests/replsets/opcounters_repl.js102
-rw-r--r--src/mongo/db/repl/oplog.cpp13
2 files changed, 114 insertions, 1 deletions
diff --git a/jstests/replsets/opcounters_repl.js b/jstests/replsets/opcounters_repl.js
new file mode 100644
index 00000000000..af45d96ae03
--- /dev/null
+++ b/jstests/replsets/opcounters_repl.js
@@ -0,0 +1,102 @@
+/**
+ * This test verifies that the "serverStatus.opcountersRepl" is incremented correctly on
+ * secondary during steady-state replication. Additionally, it also verifies the
+ * "serverStatus.opcounters" on primary to check if it exhibits the same behavior as
+ * secondary.
+ */
+
+(function() {
+ "use strict";
+
+ const testName = "opcounters_repl";
+ const dbName = testName;
+ const rst = new ReplSetTest({name: testName, nodes: 2});
+ rst.startSet();
+ rst.initiate();
+
+ const primary = rst.getPrimary();
+ const primaryDB = primary.getDB(dbName);
+ const secondary = rst.getSecondary();
+
+ const collName = "coll";
+ const collNs = dbName + '.' + collName;
+ const primaryColl = primaryDB[collName];
+
+ function getOpCounters(node) {
+ return assert.commandWorked(node.adminCommand({serverStatus: 1})).opcounters;
+ }
+
+ function getOpCountersRepl(node) {
+ return assert.commandWorked(node.adminCommand({serverStatus: 1})).opcountersRepl;
+ }
+
+ function getOpCountersDiff(cmdFn) {
+ // Get the counters before running cmdFn().
+ const primaryOpCountersBefore = getOpCounters(primary);
+ const secondaryOpCountersReplBefore = getOpCountersRepl(secondary);
+
+ // Run the cmd.
+ cmdFn();
+
+ // Get the counters after running cmdFn().
+ const primaryOpCountersAfter = getOpCounters(primary);
+ const secondaryOpCountersReplAfter = getOpCountersRepl(secondary);
+
+ // Calculate the diff
+ let primaryDiff = {};
+ let secondaryDiff = {};
+ for (let key in primaryOpCountersBefore) {
+ primaryDiff[key] = primaryOpCountersAfter[key] - primaryOpCountersBefore[key];
+ }
+
+ for (let key in secondaryOpCountersReplBefore) {
+ secondaryDiff[key] =
+ secondaryOpCountersReplAfter[key] - secondaryOpCountersReplBefore[key];
+ }
+ return {primary: primaryDiff, secondary: secondaryDiff};
+ }
+
+ // 1. Create collection.
+ let diff = getOpCountersDiff(() => {
+ assert.commandWorked(primaryDB.createCollection(collName, {writeConcern: {w: 2}}));
+ });
+ // On primary, the command counter accounts for create command and for other internal
+ // commands like replSetUpdatePosition, replSetHeartbeat, serverStatus, etc.
+ assert.gte(diff.primary.command, 1);
+ assert.eq(diff.secondary.command, 1);
+
+ // 2. Insert a document.
+ diff = getOpCountersDiff(() => {
+ assert.writeOK(primaryColl.insert({_id: 0}, {writeConcern: {w: 2}}));
+ });
+ assert.eq(diff.primary.insert, 1);
+ assert.eq(diff.secondary.insert, 1);
+
+ // 3. Update a document.
+ diff = getOpCountersDiff(() => {
+ assert.writeOK(primaryColl.update({_id: 0}, {$set: {a: 1}}, {writeConcern: {w: 2}}));
+ });
+ assert.eq(diff.primary.update, 1);
+ assert.eq(diff.secondary.update, 1);
+
+ // 4. Delete a document.
+ diff = getOpCountersDiff(() => {
+ assert.writeOK(primaryColl.remove({_id: 0}, {writeConcern: {w: 2}}));
+ });
+ assert.eq(diff.primary.delete, 1);
+ assert.eq(diff.secondary.delete, 1);
+
+ // 5. Atomic insert operation via applyOps cmd.
+ diff = getOpCountersDiff(() => {
+ assert.commandWorked(primaryColl.runCommand(
+ {applyOps: [{op: "i", ns: collNs, o: {_id: 1}}], writeConcern: {w: 2}}));
+ });
+ // On primary, the command counter accounts for applyOps command and for other internal
+ // commands like replSetUpdatePosition, replSetHeartbeat, serverStatus, etc.
+ assert.gte(diff.primary.command, 1);
+ assert.eq(diff.secondary.command, 0);
+ assert.eq(diff.primary.insert, 1);
+ assert.eq(diff.secondary.insert, 1);
+
+ rst.stopSet();
+})();
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 1cb1f3c3a13..9f90ab6e83c 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -1062,7 +1062,13 @@ Status applyOperation_inlock(OperationContext* opCtx,
LOG(3) << "applying op: " << redact(op)
<< ", oplog application mode: " << OplogApplication::modeToString(mode);
- OpCounters* opCounters = opCtx->writesAreReplicated() ? &globalOpCounters : &replOpCounters;
+ // Choose opCounters based on running on standalone/primary or secondary by checking
+ // whether writes are replicated. Atomic applyOps command is an exception, which runs
+ // on primary/standalone but disables write replication.
+ OpCounters* opCounters =
+ (mode == repl::OplogApplication::Mode::kApplyOpsCmd || opCtx->writesAreReplicated())
+ ? &globalOpCounters
+ : &replOpCounters;
std::array<StringData, 8> names = {"ts", "t", "o", "ui", "ns", "op", "b", "o2"};
std::array<BSONElement, 8> fields;
@@ -1532,6 +1538,11 @@ Status applyCommand_inlock(OperationContext* opCtx,
const char* opType = fieldOp.valuestrsafe();
invariant(*opType == 'c'); // only commands are processed here
+ // Choose opCounters based on running on standalone/primary or secondary by checking
+ // whether writes are replicated.
+ OpCounters* opCounters = opCtx->writesAreReplicated() ? &globalOpCounters : &replOpCounters;
+ opCounters->gotCommand();
+
if (fieldO.eoo()) {
return Status(ErrorCodes::NoSuchKey, "Missing expected field 'o'");
}