diff options
author | Ali Mir <ali.mir@mongodb.com> | 2021-05-21 13:38:26 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-06-15 18:32:13 +0000 |
commit | d94fcf99287475ec6d27be08d373e63732630a77 (patch) | |
tree | 5829cddfd64202f580186459c42bac19b77d514e | |
parent | 127036e004966e88807034eb1fe4f690f23b0e01 (diff) | |
download | mongo-d94fcf99287475ec6d27be08d373e63732630a77.tar.gz |
SERVER-57008 Add unit tests for reconfigs that would change implicit default write concern
(cherry picked from commit d52d91b71659a68789cae138f513307d0bb785a4)
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp index c6128a56c59..194923099a6 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_reconfig_test.cpp @@ -809,6 +809,322 @@ TEST_F(ReplCoordTest, NodeAcceptsConfigFromAReconfigWithForceTrueWhileNotPrimary ASSERT_GREATER_THAN(result.obj()["config"].Obj()["version"].numberInt(), 3); } +TEST_F(ReplCoordTest, ReconfigThatChangesIDWCFromWMajToW1WithoutCWWCSetFails) { + assertStartSuccess(BSON("_id" + << "mySet" + << "version" << 2 << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345"))), + HostAndPort("node1", 12345)); + ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + // Adding an arbiter would change the default write concern from {w: majority} to {w: 1}. + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "protocolVersion" << 1 << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345") + << BSON("_id" << 3 << "host" + << "node3:12345" + << "arbiterOnly" << true))); + + const auto opCtx = makeOperationContext(); + ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible, + getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result)); +} + +TEST_F(ReplCoordTest, ReconfigThatChangesIDWCFromW1toWMajWithoutCWWCSetFails) { + 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)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + // Removing an arbiter would change the default write concern from {w: 1} to {w: majority}. + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "protocolVersion" << 1 << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345"))); + + const auto opCtx = makeOperationContext(); + ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible, + getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result)); +} + + +TEST_F(ReplCoordTest, ReconfigThatChangesIDWCWMajToW1WithCWWCSetPasses) { + RWConcernDefault newDefaults; + WriteConcernOptions wc; + wc.wMode = "majority"; + wc.usedDefaultConstructedWC = false; + wc.notExplicitWValue = false; + newDefaults.setDefaultWriteConcern(wc); + lookupMock.setLookupCallReturnValue(std::move(newDefaults)); + + assertStartSuccess(BSON("_id" + << "mySet" + << "version" << 2 << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345"))), + HostAndPort("node1", 12345)); + ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + const auto opCtx = makeOperationContext(); + // It's not safe to assert from any other thread other than the main thread in our unit tests. + // Doing so can prevent other tests in the suite from running. As a result, we define + // 'status' here, and check its value after the reconfig thread has finished. + Status status(ErrorCodes::InternalError, "Not Set"); + stdx::thread reconfigThread([&] { + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "term" << 1 << "protocolVersion" << 1 + << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345") + << BSON("_id" << 3 << "host" + << "node3:12345" + << "arbiterOnly" << true))); + status = getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result); + }); + ReplSetHeartbeatArgsV1 hbArgs; + hbArgs.setSetName("mySet"); + hbArgs.setConfigVersion(2); + hbArgs.setSenderId(2); + hbArgs.setSenderHost(HostAndPort("node2", 12345)); + hbArgs.setTerm(0); + ReplSetHeartbeatResponse hbResp; + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + hbArgs.setSenderId(3); + hbArgs.setSenderHost(HostAndPort("node3", 12345)); + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + replyToReceivedHeartbeatV1(); + replyToReceivedHeartbeatV1(); + // As we have set the cluster-wide write concern, the reconfig should succeed. + reconfigThread.join(); + ASSERT_OK(status); +} + +TEST_F(ReplCoordTest, ReconfigThatChangesIDWCW1ToWMajWithCWWCSetPasses) { + RWConcernDefault newDefaults; + WriteConcernOptions wc; + wc.wMode = "majority"; + wc.usedDefaultConstructedWC = false; + wc.notExplicitWValue = false; + newDefaults.setDefaultWriteConcern(wc); + lookupMock.setLookupCallReturnValue(std::move(newDefaults)); + + 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)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + const auto opCtx = makeOperationContext(); + // It's not safe to assert from any other thread other than the main thread in our unit tests. + // Doing so can prevent other tests in the suite from running. As a result, we define + // 'status' here, and check its value after the reconfig thread has finished. + Status status(ErrorCodes::InternalError, "Not Set"); + stdx::thread reconfigThread([&] { + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "term" << 1 << "protocolVersion" << 1 + << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345"))); + status = getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result); + }); + ReplSetHeartbeatArgsV1 hbArgs; + hbArgs.setSetName("mySet"); + hbArgs.setConfigVersion(2); + hbArgs.setSenderId(2); + hbArgs.setSenderHost(HostAndPort("node2", 12345)); + hbArgs.setTerm(0); + ReplSetHeartbeatResponse hbResp; + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + hbArgs.setSenderId(3); + hbArgs.setSenderHost(HostAndPort("node3", 12345)); + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + replyToReceivedHeartbeatV1(); + replyToReceivedHeartbeatV1(); + // As we have set the cluster-wide write concern, the reconfig should succeed. + reconfigThread.join(); + ASSERT_OK(status); +} + +TEST_F(ReplCoordTest, ReconfigThatKeepsIDWCAtW1WithoutCWWCSetPasses) { + 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)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + const auto opCtx = makeOperationContext(); + // It's not safe to assert from any other thread other than the main thread in our unit tests. + // Doing so can prevent other tests in the suite from running. As a result, we define + // 'status' here, and check its value after the reconfig thread has finished. + Status status(ErrorCodes::InternalError, "Not Set"); + stdx::thread reconfigThread([&] { + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + // Adding another arbiter would keep the default write concern at {w: 1}. + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "term" << 1 << "protocolVersion" << 1 + << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345") + << BSON("_id" << 3 << "host" + << "node3:12345" + << "arbiterOnly" << true) + << BSON("_id" << 4 << "host" + << "node4:12345" + << "arbiterOnly" << true))); + status = getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result); + }); + ReplSetHeartbeatArgsV1 hbArgs; + hbArgs.setSetName("mySet"); + hbArgs.setConfigVersion(2); + hbArgs.setSenderId(2); + hbArgs.setSenderHost(HostAndPort("node2", 12345)); + hbArgs.setTerm(0); + ReplSetHeartbeatResponse hbResp; + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + hbArgs.setSenderId(3); + hbArgs.setSenderHost(HostAndPort("node3", 12345)); + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + hbArgs.setSenderId(4); + hbArgs.setSenderHost(HostAndPort("node4", 12345)); + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + replyToReceivedHeartbeatV1(); + replyToReceivedHeartbeatV1(); + replyToReceivedHeartbeatV1(); + reconfigThread.join(); + ASSERT_OK(status); +} + +TEST_F(ReplCoordTest, ReconfigThatKeepsIDWCAtWMajWithoutCWWCSetPasses) { + assertStartSuccess(BSON("_id" + << "mySet" + << "version" << 2 << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345"))), + HostAndPort("node1", 12345)); + ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY)); + replCoordSetMyLastAppliedOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + replCoordSetMyLastDurableOpTime(OpTime(Timestamp(100, 1), 0), Date_t() + Seconds(100)); + simulateSuccessfulV1Election(); + ASSERT_TRUE(getReplCoord()->getMemberState().primary()); + + const auto opCtx = makeOperationContext(); + // It's not safe to assert from any other thread other than the main thread in our unit tests. + // Doing so can prevent other tests in the suite from running. As a result, we define + // 'status' here, and check its value after the reconfig thread has finished. + Status status(ErrorCodes::InternalError, "Not Set"); + stdx::thread reconfigThread([&] { + BSONObjBuilder result; + ReplSetReconfigArgs args; + args.force = false; + // Adding another data-bearing node would keep the default write concern at {w: majority}. + args.newConfigObj = BSON("_id" + << "mySet" + << "version" << 3 << "term" << 1 << "protocolVersion" << 1 + << "members" + << BSON_ARRAY(BSON("_id" << 1 << "host" + << "node1:12345") + << BSON("_id" << 2 << "host" + << "node2:12345") + << BSON("_id" << 3 << "host" + << "node3:12345"))); + status = getReplCoord()->processReplSetReconfig(opCtx.get(), args, &result); + }); + ReplSetHeartbeatArgsV1 hbArgs; + hbArgs.setSetName("mySet"); + hbArgs.setConfigVersion(2); + hbArgs.setSenderId(2); + hbArgs.setSenderHost(HostAndPort("node2", 12345)); + hbArgs.setTerm(0); + ReplSetHeartbeatResponse hbResp; + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + hbArgs.setSenderId(3); + hbArgs.setSenderHost(HostAndPort("node3", 12345)); + ASSERT_OK(getReplCoord()->processHeartbeatV1(hbArgs, &hbResp)); + replyToReceivedHeartbeatV1(); + replyToReceivedHeartbeatV1(); + reconfigThread.join(); + ASSERT_OK(status); +} + class ReplCoordReconfigTest : public ReplCoordTest { public: int counter = 0; |