summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl/replication_coordinator_impl_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/repl/replication_coordinator_impl_test.cpp')
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_test.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
index 6dfaebf5287..4fd00274f8f 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
@@ -44,6 +44,8 @@
#include "mongo/db/concurrency/replication_state_transition_lock_guard.h"
#include "mongo/db/operation_context_noop.h"
#include "mongo/db/repl/bson_extract_optime.h"
+#include "mongo/db/repl/data_replicator_external_state_impl.h"
+#include "mongo/db/repl/heartbeat_response_action.h"
#include "mongo/db/repl/is_master_response.h"
#include "mongo/db/repl/optime.h"
#include "mongo/db/repl/read_concern_args.h"
@@ -7803,6 +7805,162 @@ TEST_F(ReplCoordTest, NodeFailsVoteRequestIfCandidateIndexIsInvalid) {
}
}
+TEST_F(ReplCoordTest, IgnoreNonNullDurableOpTimeOrWallTimeForArbiterFromReplSetUpdatePosition) {
+ init("mySet/node1:12345,node2:12345,node3:12345");
+ assertStartSuccess(BSON("_id"
+ << "mySet"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "node1:12345")
+ << BSON("_id" << 2 << "host"
+ << "node2:12345")
+ << BSON("_id" << 3 << "host"
+ << "node3:12345"
+ << "arbiterOnly" << true))),
+ HostAndPort("node1", 12345));
+ ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY));
+ const auto repl = getReplCoord();
+
+ OpTimeWithTermOne opTime1(100, 1);
+ OpTimeWithTermOne opTime2(200, 2);
+ Date_t wallTime1 = Date_t() + Seconds(10);
+ Date_t wallTime2 = Date_t() + Seconds(20);
+
+ // Node 1 is ahead, nodes 2 and 3 a bit behind.
+ // Node 3 should not have a durable optime/walltime as they are an arbiter.
+ repl->setMyLastAppliedOpTimeAndWallTime({opTime2, wallTime2});
+ repl->setMyLastDurableOpTimeAndWallTime({opTime2, wallTime2});
+ ASSERT_OK(repl->setLastAppliedOptime_forTest(1, 2, opTime1, wallTime1));
+ ASSERT_OK(repl->setLastAppliedOptime_forTest(1, 3, opTime1, wallTime1));
+ ASSERT_OK(repl->setLastDurableOptime_forTest(1, 2, opTime1, wallTime1));
+ ASSERT_OK(repl->setLastDurableOptime_forTest(1, 3, OpTime(), Date_t()));
+
+ simulateSuccessfulV1Election();
+ ASSERT_TRUE(repl->getMemberState().primary());
+
+ // Receive updates on behalf of nodes 2 and 3 from node 2.
+ // Node 3 will be reported as caught up both in lastApplied and lastDurable, but we
+ // must ignore the lastDurable part as null is the only valid value for arbiters.
+ long long configVersion = repl->getConfig().getConfigVersion();
+ UpdatePositionArgs updatePositionArgs;
+
+ ASSERT_OK(updatePositionArgs.initialize(BSON(
+ UpdatePositionArgs::kCommandFieldName
+ << 1 << UpdatePositionArgs::kUpdateArrayFieldName
+ << BSON_ARRAY(
+ BSON(UpdatePositionArgs::kConfigVersionFieldName
+ << repl->getConfig().getConfigVersion()
+ << UpdatePositionArgs::kMemberIdFieldName << 2
+ << UpdatePositionArgs::kAppliedOpTimeFieldName << opTime2.asOpTime().toBSON()
+ << UpdatePositionArgs::kAppliedWallTimeFieldName << wallTime2
+ << UpdatePositionArgs::kDurableOpTimeFieldName << opTime2.asOpTime().toBSON()
+ << UpdatePositionArgs::kDurableWallTimeFieldName << wallTime2)
+ << BSON(UpdatePositionArgs::kConfigVersionFieldName
+ << repl->getConfig().getConfigVersion()
+ << UpdatePositionArgs::kMemberIdFieldName << 3
+ << UpdatePositionArgs::kAppliedOpTimeFieldName << opTime2.asOpTime().toBSON()
+ << UpdatePositionArgs::kAppliedWallTimeFieldName << wallTime2
+ << UpdatePositionArgs::kDurableOpTimeFieldName << opTime2.asOpTime().toBSON()
+ << UpdatePositionArgs::kDurableWallTimeFieldName << wallTime2)))));
+
+ startCapturingLogMessages();
+ ASSERT_OK(repl->processReplSetUpdatePosition(updatePositionArgs, &configVersion));
+
+ // Make sure node 2 is fully caught up but node 3 has null durable optime/walltime.
+ auto memberDataVector = repl->getMemberData();
+ for (auto member : memberDataVector) {
+ auto memberId = member.getMemberId();
+ if (memberId == MemberId(1) || memberId == MemberId(2)) {
+ ASSERT_EQ(member.getLastAppliedOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastAppliedWallTime(), wallTime2);
+ ASSERT_EQ(member.getLastDurableOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastDurableWallTime(), wallTime2);
+ continue;
+ } else if (member.getMemberId() == MemberId(3)) {
+ ASSERT_EQ(member.getLastAppliedOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastAppliedWallTime(), wallTime2);
+ ASSERT_EQ(member.getLastDurableOpTime(), OpTime());
+ ASSERT_EQ(member.getLastDurableWallTime(), Date_t());
+ continue;
+ }
+ MONGO_UNREACHABLE;
+ }
+ stopCapturingLogMessages();
+ ASSERT_EQUALS(
+ 1,
+ countTextFormatLogLinesContaining(
+ "Received non-null durable optime/walltime for arbiter from replSetUpdatePosition"));
+}
+
+TEST_F(ReplCoordTest, IgnoreNonNullDurableOpTimeOrWallTimeForArbiterFromHeartbeat) {
+ unittest::MinimumLoggedSeverityGuard severityGuard{logv2::LogComponent::kReplication,
+ logv2::LogSeverity::Debug(1)};
+ init("mySet/node1:12345,node2:12345");
+ assertStartSuccess(BSON("_id"
+ << "mySet"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "node1:12345")
+ << BSON("_id" << 2 << "host"
+ << "node2:12345"
+ << "arbiterOnly" << true))),
+ HostAndPort("node1", 12345));
+ ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY));
+ const auto repl = getReplCoord();
+
+ OpTimeWithTermOne opTime1(100, 1);
+ OpTimeWithTermOne opTime2(200, 2);
+ Date_t wallTime1 = Date_t() + Seconds(10);
+ Date_t wallTime2 = Date_t() + Seconds(20);
+
+ // Node 1 is ahead, nodes 2 is a bit behind.
+ // Node 2 should not have a durable optime/walltime as they are an arbiter.
+ repl->setMyLastAppliedOpTimeAndWallTime({opTime2, wallTime2});
+ repl->setMyLastDurableOpTimeAndWallTime({opTime2, wallTime2});
+ ASSERT_OK(repl->setLastAppliedOptime_forTest(1, 2, opTime1, wallTime1));
+ ASSERT_OK(repl->setLastDurableOptime_forTest(1, 2, OpTime(), Date_t()));
+
+ simulateSuccessfulV1Election();
+ ASSERT_TRUE(repl->getMemberState().primary());
+
+ ReplSetHeartbeatResponse hbResp;
+ hbResp.setSetName("mySet");
+ hbResp.setTerm(1);
+ hbResp.setConfigVersion(2);
+ hbResp.setConfigTerm(1);
+ hbResp.setState(MemberState::RS_ARBITER);
+ hbResp.setAppliedOpTimeAndWallTime({opTime2, wallTime2});
+ hbResp.setDurableOpTimeAndWallTime({opTime2, wallTime2});
+
+ startCapturingLogMessages();
+ repl->handleHeartbeatResponse_forTest(
+ hbResp.toBSON(), 1 /* targetIndex */, Milliseconds(5) /* ping */);
+
+ auto memberDataVector = repl->getMemberData();
+ for (auto member : memberDataVector) {
+ auto memberId = member.getMemberId();
+ if (memberId == MemberId(1)) {
+ ASSERT_EQ(member.getLastAppliedOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastAppliedWallTime(), wallTime2);
+ ASSERT_EQ(member.getLastDurableOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastDurableWallTime(), wallTime2);
+ continue;
+ } else if (member.getMemberId() == MemberId(2)) {
+ ASSERT_EQ(member.getLastAppliedOpTime(), opTime2.asOpTime());
+ ASSERT_EQ(member.getLastAppliedWallTime(), wallTime2);
+ ASSERT_EQ(member.getLastDurableOpTime(), OpTime());
+ ASSERT_EQ(member.getLastDurableWallTime(), Date_t());
+ continue;
+ }
+ MONGO_UNREACHABLE;
+ }
+
+ stopCapturingLogMessages();
+ ASSERT_EQUALS(1,
+ countTextFormatLogLinesContaining(
+ "Received non-null durable optime/walltime for arbiter from heartbeat"));
+}
+
// TODO(schwerin): Unit test election id updating
} // namespace
} // namespace repl