diff options
author | Suganthi Mani <suganthi.mani@mongodb.com> | 2018-12-06 17:55:25 -0500 |
---|---|---|
committer | Suganthi Mani <suganthi.mani@mongodb.com> | 2019-01-12 15:05:56 -0500 |
commit | 89d845e752312fce0e12be4cf8f9761e44adc004 (patch) | |
tree | d5542d992e91fd168cb6f3ccd35f63138e1e2cdc | |
parent | a722798ac1732b18239b808b5363be0a1b16d166 (diff) | |
download | mongo-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.js | 102 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog.cpp | 13 |
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'"); } |