summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorPavithra Vetriselvan <pavithra.vetriselvan@mongodb.com>2020-01-30 01:16:44 +0000
committerevergreen <evergreen@mongodb.com>2020-01-30 01:16:44 +0000
commit101eb1694568df1ff086a460cc6c511eacc05860 (patch)
tree94947ab0fbc02c2160fce771abe0b9cb0331b80a /src/mongo/db
parentbeb9ffb3b326e22bea6e34abf17be8a2bc53df0a (diff)
downloadmongo-101eb1694568df1ff086a460cc6c511eacc05860.tar.gz
SERVER-44939 allow replication across config versions
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp8
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_test.cpp77
-rw-r--r--src/mongo/db/repl/topology_coordinator.cpp43
-rw-r--r--src/mongo/db/repl/topology_coordinator_v1_test.cpp44
-rw-r--r--src/mongo/db/server_options.h5
5 files changed, 117 insertions, 60 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 6be06869253..b14c37585ca 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -2610,8 +2610,12 @@ void ReplicationCoordinatorImpl::cancelAndRescheduleElectionTimeout() {
EventHandle ReplicationCoordinatorImpl::_processReplSetMetadata_inlock(
const rpc::ReplSetMetadata& replMetadata) {
- if (replMetadata.getConfigVersion() != _rsConfig.getConfigVersion()) {
- return EventHandle();
+ // If we're in FCV 4.4, allow metadata updates between config versions.
+ if (!serverGlobalParams.featureCompatibility.isVersion(
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo44)) {
+ if (replMetadata.getConfigVersion() != _rsConfig.getConfigVersion()) {
+ return EventHandle();
+ }
}
return _updateTerm_inlock(replMetadata.getTerm());
}
diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
index 71c709f0727..15a6b44532d 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
@@ -3592,7 +3592,7 @@ TEST_F(ReplCoordTest, DoNotProcessSelfWhenUpdatePositionContainsInfoAboutSelf) {
getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern).status);
}
-TEST_F(ReplCoordTest, DoNotProcessUpdatePositionWhenItsConfigVersionIsIncorrect) {
+TEST_F(ReplCoordTest, ProcessUpdatePositionWhenItsConfigVersionIsDifferent) {
assertStartSuccess(BSON("_id"
<< "mySet"
<< "version" << 2 << "members"
@@ -3601,10 +3601,7 @@ TEST_F(ReplCoordTest, DoNotProcessUpdatePositionWhenItsConfigVersionIsIncorrect)
<< "_id" << 0)
<< BSON("host"
<< "node2:12345"
- << "_id" << 1)
- << BSON("host"
- << "node3:12345"
- << "_id" << 2))),
+ << "_id" << 1))),
HostAndPort("node1", 12345));
ASSERT_OK(getReplCoord()->setFollowerMode(MemberState::RS_SECONDARY));
replCoordSetMyLastAppliedOpTime(OpTimeWithTermOne(100, 1), Date_t() + Seconds(100));
@@ -3620,28 +3617,27 @@ TEST_F(ReplCoordTest, DoNotProcessUpdatePositionWhenItsConfigVersionIsIncorrect)
writeConcern.wTimeout = WriteConcernOptions::kNoWaiting;
writeConcern.wNumNodes = 1;
- // receive updatePosition with incorrect config version
+ // receive updatePosition with a different config version, 3
+ replCoordSetMyLastAppliedOpTime(time2, Date_t() + Seconds(100));
+ replCoordSetMyLastDurableOpTime(time2, Date_t() + Seconds(100));
+ auto updatePositionConfigVersion = 3;
UpdatePositionArgs args;
- ASSERT_OK(args.initialize(
- BSON(UpdatePositionArgs::kCommandFieldName
- << 1 << UpdatePositionArgs::kUpdateArrayFieldName
- << BSON_ARRAY(BSON(UpdatePositionArgs::kConfigVersionFieldName
- << 3 << UpdatePositionArgs::kMemberIdFieldName << 1
- << UpdatePositionArgs::kDurableOpTimeFieldName << time2.toBSON()
- << UpdatePositionArgs::kDurableWallTimeFieldName
- << Date_t() + Seconds(time2.getSecs())
- << UpdatePositionArgs::kAppliedOpTimeFieldName << time2.toBSON()
- << UpdatePositionArgs::kAppliedWallTimeFieldName
- << Date_t() + Seconds(time2.getSecs()))))));
+ ASSERT_OK(args.initialize(BSON(
+ UpdatePositionArgs::kCommandFieldName
+ << 1 << UpdatePositionArgs::kUpdateArrayFieldName
+ << BSON_ARRAY(BSON(UpdatePositionArgs::kConfigVersionFieldName
+ << updatePositionConfigVersion << UpdatePositionArgs::kMemberIdFieldName
+ << 1 << UpdatePositionArgs::kDurableOpTimeFieldName << time2.toBSON()
+ << UpdatePositionArgs::kDurableWallTimeFieldName
+ << Date_t() + Seconds(time2.getSecs())
+ << UpdatePositionArgs::kAppliedOpTimeFieldName << time2.toBSON()
+ << UpdatePositionArgs::kAppliedWallTimeFieldName
+ << Date_t() + Seconds(time2.getSecs()))))));
auto opCtx = makeOperationContext();
-
- long long cfgver;
- ASSERT_EQUALS(ErrorCodes::InvalidReplicaSetConfig,
- getReplCoord()->processReplSetUpdatePosition(args, &cfgver));
- ASSERT_EQUALS(ErrorCodes::WriteConcernFailed,
- getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern).status);
+ ASSERT_OK(getReplCoord()->processReplSetUpdatePosition(args, nullptr));
+ ASSERT_OK(getReplCoord()->awaitReplication(opCtx.get(), time2, writeConcern).status);
}
TEST_F(ReplCoordTest, DoNotProcessUpdatePositionOfMembersWhoseIdsAreNotInTheConfig) {
@@ -5023,7 +5019,7 @@ TEST_F(ReplCoordTest, WaitUntilOpTimeforReadRejectsUnsupportedMajorityReadConcer
ASSERT_OK(status);
}
-TEST_F(ReplCoordTest, IgnoreTheContentsOfMetadataWhenItsConfigVersionDoesNotMatchOurs) {
+TEST_F(ReplCoordTest, DoNotIgnoreTheContentsOfMetadataWhenItsConfigVersionDoesNotMatchOurs) {
// Ensure that we do not process ReplSetMetadata when ConfigVersions do not match.
assertStartSuccess(BSON("_id"
<< "mySet"
@@ -5041,25 +5037,30 @@ TEST_F(ReplCoordTest, IgnoreTheContentsOfMetadataWhenItsConfigVersionDoesNotMatc
ASSERT_EQUALS(OpTime(Timestamp(0, 0), 0), getReplCoord()->getLastCommittedOpTime());
// lower configVersion
+ auto lowerConfigVersion = 1;
StatusWith<rpc::ReplSetMetadata> metadata = rpc::ReplSetMetadata::readFromMetadata(BSON(
- rpc::kReplSetMetadataFieldName << BSON(
- "lastOpCommitted" << BSON("ts" << Timestamp(10, 0) << "t" << 2) << "lastCommittedWall"
- << Date_t() + Seconds(100) << "lastOpVisible"
- << BSON("ts" << Timestamp(10, 0) << "t" << 2) << "configVersion" << 1
- << "primaryIndex" << 2 << "term" << 2 << "syncSourceIndex" << 1)));
+ rpc::kReplSetMetadataFieldName
+ << BSON("lastOpCommitted" << BSON("ts" << Timestamp(10, 0) << "t" << 2)
+ << "lastCommittedWall" << Date_t() + Seconds(100)
+ << "lastOpVisible" << BSON("ts" << Timestamp(10, 0) << "t" << 2)
+ << "configVersion" << lowerConfigVersion << "primaryIndex" << 2
+ << "term" << 2 << "syncSourceIndex" << 1)));
getReplCoord()->processReplSetMetadata(metadata.getValue());
- ASSERT_EQUALS(0, getReplCoord()->getTerm());
+ // term should advance
+ ASSERT_EQUALS(2, getReplCoord()->getTerm());
// higher configVersion
- StatusWith<rpc::ReplSetMetadata> metadata2 = rpc::ReplSetMetadata::readFromMetadata(
- BSON(rpc::kReplSetMetadataFieldName
- << BSON("lastOpCommitted"
- << BSON("ts" << Timestamp(10, 0) << "t" << 2) << "lastCommittedWall"
- << Date_t() + Seconds(100) << "lastOpVisible"
- << BSON("ts" << Timestamp(10, 0) << "t" << 2) << "configVersion" << 100
- << "primaryIndex" << 2 << "term" << 2 << "syncSourceIndex" << 1)));
+ auto higherConfigVersion = 100;
+ StatusWith<rpc::ReplSetMetadata> metadata2 = rpc::ReplSetMetadata::readFromMetadata(BSON(
+ rpc::kReplSetMetadataFieldName
+ << BSON("lastOpCommitted" << BSON("ts" << Timestamp(10, 0) << "t" << 2)
+ << "lastCommittedWall" << Date_t() + Seconds(100)
+ << "lastOpVisible" << BSON("ts" << Timestamp(10, 0) << "t" << 2)
+ << "configVersion" << higherConfigVersion << "primaryIndex" << 2
+ << "term" << 2 << "syncSourceIndex" << 1)));
getReplCoord()->processReplSetMetadata(metadata2.getValue());
- ASSERT_EQUALS(0, getReplCoord()->getTerm());
+ // term should advance
+ ASSERT_EQUALS(2, getReplCoord()->getTerm());
}
TEST_F(ReplCoordTest, UpdateLastCommittedOpTimeWhenTheLastCommittedOpTimeIsNewer) {
diff --git a/src/mongo/db/repl/topology_coordinator.cpp b/src/mongo/db/repl/topology_coordinator.cpp
index e1817bca636..770440e7c1c 100644
--- a/src/mongo/db/repl/topology_coordinator.cpp
+++ b/src/mongo/db/repl/topology_coordinator.cpp
@@ -1071,16 +1071,22 @@ StatusWith<bool> TopologyCoordinator::setLastOptime(const UpdatePositionArgs::Up
<< " has reached optime: " << args.appliedOpTime
<< " and is durable through: " << args.durableOpTime;
- if (args.cfgver != _rsConfig.getConfigVersion()) {
- std::string errmsg = str::stream()
- << "Received replSetUpdatePosition for node with memberId " << memberId
- << " whose config version of " << args.cfgver << " doesn't match our config version of "
- << _rsConfig.getConfigVersion();
- LOG(1) << errmsg;
- *configVersion = _rsConfig.getConfigVersion();
- return Status(ErrorCodes::InvalidReplicaSetConfig, errmsg);
- }
-
+ // If we're in FCV 4.4, allow replSetUpdatePosition commands between config versions.
+ if (!serverGlobalParams.featureCompatibility.isVersion(
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo44)) {
+ if (args.cfgver != _rsConfig.getConfigVersion()) {
+ std::string errmsg = str::stream()
+ << "Received replSetUpdatePosition for node with memberId " << memberId
+ << " whose config version of " << args.cfgver
+ << " doesn't match our config version of " << _rsConfig.getConfigVersion();
+ LOG(1) << errmsg;
+ *configVersion = _rsConfig.getConfigVersion();
+ return Status(ErrorCodes::InvalidReplicaSetConfig, errmsg);
+ }
+ }
+
+ // While we can accept replSetUpdatePosition commands across config versions, we still do not
+ // allow receiving them from a node that is not in our config.
auto* memberData = _findMemberDataByMemberId(memberId.getData());
if (!memberData) {
invariant(!_rsConfig.findMemberByID(memberId.getData()));
@@ -2612,13 +2618,20 @@ bool TopologyCoordinator::shouldChangeSyncSource(
return true;
}
- if (replMetadata.getConfigVersion() != _rsConfig.getConfigVersion()) {
- log() << "Choosing new sync source because the config version supplied by " << currentSource
- << ", " << replMetadata.getConfigVersion() << ", does not match ours, "
- << _rsConfig.getConfigVersion();
- return true;
+ // If we're in FCV 4.4, allow data replication between config versions. Otherwise, change
+ // our sync source.
+ if (!serverGlobalParams.featureCompatibility.isVersion(
+ ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo44)) {
+ if (replMetadata.getConfigVersion() != _rsConfig.getConfigVersion()) {
+ log() << "Choosing new sync source because the config version supplied by "
+ << currentSource << ", " << replMetadata.getConfigVersion()
+ << ", does not match ours, " << _rsConfig.getConfigVersion();
+ return true;
+ }
}
+ // While we can allow data replication across config versions, we still do not allow syncing
+ // from a node that is not in our config.
const int currentSourceIndex = _rsConfig.findMemberIndexByHostAndPort(currentSource);
if (currentSourceIndex == -1) {
log() << "Choosing new sync source because " << currentSource.toString()
diff --git a/src/mongo/db/repl/topology_coordinator_v1_test.cpp b/src/mongo/db/repl/topology_coordinator_v1_test.cpp
index 54519ab8390..4720e7b01b4 100644
--- a/src/mongo/db/repl/topology_coordinator_v1_test.cpp
+++ b/src/mongo/db/repl/topology_coordinator_v1_test.cpp
@@ -208,11 +208,13 @@ protected:
// Only set visibleOpTime, primaryIndex and syncSourceIndex
ReplSetMetadata makeReplSetMetadata(OpTime visibleOpTime = OpTime(),
int primaryIndex = -1,
- int syncSourceIndex = -1) {
+ int syncSourceIndex = -1,
+ long long configVersion = -1) {
+ auto configIn = (configVersion != -1) ? configVersion : _currentConfig.getConfigVersion();
return ReplSetMetadata(_topo->getTerm(),
OpTimeAndWallTime(),
visibleOpTime,
- _currentConfig.getConfigVersion(),
+ configIn,
OID(),
primaryIndex,
syncSourceIndex);
@@ -5627,6 +5629,44 @@ TEST_F(HeartbeatResponseTestV1, ShouldNotChangeSyncSourceWhenFresherMemberIsNotR
HostAndPort("host2"), makeReplSetMetadata(syncSourceOpTime), boost::none, now()));
}
+TEST_F(HeartbeatResponseTestV1, ShouldNotChangeSyncSourceIfSyncSourceHasDifferentConfigVersion) {
+ // In this test, the TopologyCoordinator should not tell us to change sync sources away from
+ // "host2" because it has a different config version.
+ OpTime election = OpTime();
+ // Our last op time applied must be behind host2, or we'll hit the case where we change
+ // sync sources due to the sync source being behind, without a sync source, and not primary.
+ OpTime lastOpTimeApplied = OpTime(Timestamp(400, 0), 0);
+ OpTime syncSourceOpTime = OpTime(Timestamp(400, 1), 0);
+
+ // Update the config version to 7.
+ updateConfig(BSON("_id"
+ << "rs0"
+ << "version" << 7 << "term" << 2LL << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "hself")
+ << BSON("_id" << 1 << "host"
+ << "host2"))),
+ 0);
+
+ topoCoordSetMyLastAppliedOpTime(lastOpTimeApplied, Date_t(), false);
+ HeartbeatResponseAction nextAction = receiveUpHeartbeat(
+ HostAndPort("host2"), "rs0", MemberState::RS_SECONDARY, election, syncSourceOpTime);
+ ASSERT_NO_ACTION(nextAction.getAction());
+
+ // We should not want to change our sync source from "host2" even though it has a different
+ // config version.
+ ASSERT_FALSE(getTopoCoord().shouldChangeSyncSource(
+ HostAndPort("host2"),
+ makeReplSetMetadata(OpTime(), -1, -1, 8 /* different config version */),
+ makeOplogQueryMetadata(syncSourceOpTime),
+ now()));
+ ASSERT_FALSE(
+ getTopoCoord().shouldChangeSyncSource(HostAndPort("host2"),
+ makeReplSetMetadata(syncSourceOpTime, -1, -1, 8),
+ boost::none,
+ now()));
+}
+
class HeartbeatResponseTestOneRetryV1 : public HeartbeatResponseTestV1 {
public:
virtual void setUp() {
diff --git a/src/mongo/db/server_options.h b/src/mongo/db/server_options.h
index 2a7796f9307..076f8decb87 100644
--- a/src/mongo/db/server_options.h
+++ b/src/mongo/db/server_options.h
@@ -228,9 +228,8 @@ struct ServerGlobalParams {
return _version.store(version);
}
- bool isVersionUpgradingOrUpgraded() {
- return (getVersion() == Version::kUpgradingTo44 ||
- getVersion() == Version::kFullyUpgradedTo44);
+ bool isVersion(Version version) {
+ return _version.load() == version;
}
private: