diff options
Diffstat (limited to 'src/mongo/db/repl/replication_coordinator_impl_test.cpp')
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl_test.cpp | 158 |
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 |