summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/backports_required_for_multiversion_tests.yml2
-rw-r--r--jstests/replsets/replSetGetStatus_member_wall_times.js84
-rw-r--r--src/mongo/db/repl/topology_coordinator.cpp5
3 files changed, 91 insertions, 0 deletions
diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml
index 554d06a3748..f4a30d5ff8e 100644
--- a/etc/backports_required_for_multiversion_tests.yml
+++ b/etc/backports_required_for_multiversion_tests.yml
@@ -177,6 +177,8 @@ all:
test_file: jstests/replsets/stepdown_race_with_transaction.js
- ticket: SERVER-58636
test_file: jstests/replsets/initial_sync_replicate_drop_mid_secondary_batch.js
+ - ticket: SERVER-54909
+ test_file: jstests/replsets/replSetGetStatus_member_wall_times.js
suites:
diff --git a/jstests/replsets/replSetGetStatus_member_wall_times.js b/jstests/replsets/replSetGetStatus_member_wall_times.js
new file mode 100644
index 00000000000..4cd976a752b
--- /dev/null
+++ b/jstests/replsets/replSetGetStatus_member_wall_times.js
@@ -0,0 +1,84 @@
+/**
+ * Tests that replSetGetStatus responses include the last applied and durable wall times for other
+ * members.
+ *
+ * @tags: [multiversion_incompatible]
+ */
+
+(function() {
+"use strict";
+load("jstests/libs/write_concern_util.js");
+load("jstests/replsets/rslib.js");
+
+// We use GTE to account for the possibility of other writes in the system (e.g. HMAC).
+// Comparison is GTE by default, GT if 'strict' is specified.
+function checkWallTimes(primary, greaterMemberIndex, lesserMemberIndex, strict = false) {
+ let res = assert.commandWorked(primary.adminCommand({replSetGetStatus: 1}));
+ assert(res.members, () => tojson(res));
+
+ const greater = res.members[greaterMemberIndex];
+ assert(greater, () => tojson(res));
+ const greaterApplied = greater.lastAppliedWallTime;
+ const greaterDurable = greater.lastAppliedWallTime;
+ assert(greaterApplied, () => tojson(res));
+ assert(greaterDurable, () => tojson(res));
+
+ const lesser = res.members[lesserMemberIndex];
+ assert(lesser, () => tojson(res));
+ const lesserApplied = lesser.lastAppliedWallTime;
+ const lesserDurable = lesser.lastDurableWallTime;
+ assert(lesser.lastAppliedWallTime, () => tojson(res));
+ assert(lesser.lastDurableWallTime, () => tojson(res));
+
+ if (!strict) {
+ assert.gte(greaterApplied, lesserApplied, () => tojson(res));
+ assert.gte(greaterDurable, lesserDurable, () => tojson(res));
+ } else {
+ assert.gt(greaterApplied, lesserApplied, () => tojson(res));
+ assert.gt(greaterDurable, lesserDurable, () => tojson(res));
+ }
+}
+
+const name = jsTestName();
+const rst = new ReplSetTest({name: name, nodes: 3, settings: {chainingAllowed: false}});
+
+rst.startSet();
+rst.initiateWithHighElectionTimeout();
+rst.awaitReplication();
+
+const primary = rst.getPrimary(); // node 0
+const [caughtUpSecondary, laggedSecondary] = rst.getSecondaries(); // nodes 1 and 2
+
+const dbName = "testdb";
+const collName = "testcoll";
+const primaryDB = primary.getDB(dbName);
+const primaryColl = primaryDB.getCollection(collName);
+
+jsTestLog("Creating test collection");
+assert.commandWorked(primaryColl.insert({"one": 1}));
+rst.awaitReplication();
+
+checkWallTimes(primary, 0 /* greater */, 1 /* lesser */);
+checkWallTimes(primary, 0 /* greater */, 2 /* lesser */);
+
+jsTestLog("Stopping replication on secondary: " + laggedSecondary.host);
+stopServerReplication(laggedSecondary);
+
+jsTestLog("Adding more documents to collection");
+assert.commandWorked(primaryColl.insert({"two": 2}, {writeConcern: {w: 1}}));
+rst.awaitReplication(
+ undefined /* timeout */, undefined /* secondaryOpTimeType */, [caughtUpSecondary]);
+
+// Wall times of the lagged secondary should be strictly lesser.
+checkWallTimes(primary, 0 /* greater */, 2 /* lesser */, true /* strict */);
+checkWallTimes(primary, 1 /* greater */, 2 /* lesser */, true /* strict */);
+
+jsTestLog("Letting lagged secondary catch up");
+restartServerReplication(laggedSecondary);
+rst.awaitReplication();
+
+checkWallTimes(primary, 0 /* greater */, 1 /* lesser */);
+checkWallTimes(primary, 0 /* greater */, 2 /* lesser */);
+
+rst.stopSet();
+})();
diff --git a/src/mongo/db/repl/topology_coordinator.cpp b/src/mongo/db/repl/topology_coordinator.cpp
index 1030898b642..cec008da77b 100644
--- a/src/mongo/db/repl/topology_coordinator.cpp
+++ b/src/mongo/db/repl/topology_coordinator.cpp
@@ -1789,6 +1789,8 @@ void TopologyCoordinator::prepareStatusResponse(const ReplSetStatusArgs& rsStatu
appendOpTime(&bb, "optime", lastOpApplied);
bb.appendDate("optimeDate",
Date_t::fromDurationSinceEpoch(Seconds(lastOpApplied.getSecs())));
+ bb.appendDate("lastAppliedWallTime", it->getLastAppliedWallTime());
+ bb.appendDate("lastDurableWallTime", it->getLastDurableWallTime());
}
if (!_syncSource.empty() && !_iAmPrimary()) {
@@ -1847,6 +1849,9 @@ void TopologyCoordinator::prepareStatusResponse(const ReplSetStatusArgs& rsStatu
bb.appendDate("optimeDurableDate",
Date_t::fromDurationSinceEpoch(
Seconds(it->getHeartbeatDurableOpTime().getSecs())));
+
+ bb.appendDate("lastAppliedWallTime", it->getLastAppliedWallTime());
+ bb.appendDate("lastDurableWallTime", it->getLastDurableWallTime());
}
bb.appendDate("lastHeartbeat", it->getLastHeartbeat());
bb.appendDate("lastHeartbeatRecv", it->getLastHeartbeatRecv());