summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAli Mir <ali.mir@mongodb.com>2020-11-04 17:25:32 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-01-28 04:26:57 +0000
commit35a6a97338930d75a7f5cfd89671ae9af65aaf85 (patch)
treed0f026c6f06c0a38795fa4eaa56040d145034679 /src
parent5a5a75e4149faae8f1b6e95d60b84cc7f33d4e2b (diff)
downloadmongo-35a6a97338930d75a7f5cfd89671ae9af65aaf85.tar.gz
SERVER-50423 Change memberConfig's slaveDelay field to secondaryDelaySecs
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/commands/set_feature_compatibility_version_command.cpp73
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/member_config.cpp10
-rw-r--r--src/mongo/db/repl/member_config.h25
-rw-r--r--src/mongo/db/repl/member_config.idl13
-rw-r--r--src/mongo/db/repl/member_config_test.cpp128
-rw-r--r--src/mongo/db/repl/repl_server_parameters.idl6
-rw-r--r--src/mongo/db/repl/repl_set_config.cpp42
-rw-r--r--src/mongo/db/repl/repl_set_config.h21
-rw-r--r--src/mongo/db/repl/repl_set_config_checks.cpp59
-rw-r--r--src/mongo/db/repl/repl_set_config_test.cpp46
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp2
-rw-r--r--src/mongo/db/repl/topology_coordinator.cpp19
-rw-r--r--src/mongo/db/repl/topology_coordinator_v1_test.cpp15
-rw-r--r--src/mongo/dbtests/mock/mock_replica_set.cpp6
15 files changed, 415 insertions, 51 deletions
diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
index 7f73024c99d..cbb1a42b858 100644
--- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
+++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
@@ -50,6 +50,8 @@
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/db/repl/repl_client_info.h"
+#include "mongo/db/repl/repl_server_parameters_gen.h"
+#include "mongo/db/repl/repl_set_config.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/s/active_migrations_registry.h"
#include "mongo/db/s/config/sharding_catalog_manager.h"
@@ -239,11 +241,47 @@ public:
FeatureCompatibilityVersion::validateSetFeatureCompatibilityVersionRequest(
actualVersion, requestedVersion, isFromConfigServer);
+ auto replCoord = repl::ReplicationCoordinator::get(opCtx);
+ const bool isReplSet =
+ replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet;
if (actualVersion < requestedVersion) {
checkInitialSyncFinished(opCtx);
FeatureCompatibilityVersion::updateFeatureCompatibilityVersionDocument(
opCtx, actualVersion, requestedVersion, isFromConfigServer);
+
+ // If the 'useSecondaryDelaySecs' feature flag is enabled in the upgraded FCV, issue a
+ // reconfig to change the 'slaveDelay' field to 'secondaryDelaySecs'.
+ if (isReplSet && repl::feature_flags::gUseSecondaryDelaySecs.isEnabledAndIgnoreFCV() &&
+ requestedVersion == ServerGlobalParams::FeatureCompatibility::kLatest) {
+ auto getNewConfig = [&](const repl::ReplSetConfig& oldConfig, long long term) {
+ auto newConfig = oldConfig.getMutable();
+ newConfig.setConfigVersion(newConfig.getConfigVersion() + 1);
+ for (auto mem = oldConfig.membersBegin(); mem != oldConfig.membersEnd();
+ mem++) {
+ newConfig.useSecondaryDelaySecsFieldName(mem->getId());
+ }
+ return repl::ReplSetConfig(std::move(newConfig));
+ };
+ auto status = replCoord->doReplSetReconfig(opCtx, getNewConfig, true /* force */);
+ uassertStatusOKWithContext(status, "Failed to upgrade the replica set config");
+
+ LOGV2(5042301,
+ "Waiting for the upgraded replica set config to propagate to a majority");
+ // If a write concern is given, we'll use its wTimeout. It's kNoTimeout by default.
+ WriteConcernOptions writeConcern(
+ repl::ReplSetConfig::kConfigMajorityWriteConcernModeName,
+ WriteConcernOptions::SyncMode::NONE,
+ opCtx->getWriteConcern().wTimeout);
+ writeConcern.checkCondition = WriteConcernOptions::CheckCondition::Config;
+ repl::OpTime fakeOpTime(Timestamp(1, 1), replCoord->getTerm());
+ uassertStatusOKWithContext(
+ replCoord->awaitReplication(opCtx, fakeOpTime, writeConcern).status,
+ "Failed to wait for the upgraded replica set config to propagate to a "
+ "majority");
+ LOGV2(5042302, "The upgraded replica set config has been propagated to a majority");
+ }
+
{
// Take the global lock in S mode to create a barrier for operations taking the
// global IX or X locks. This ensures that either
@@ -322,6 +360,41 @@ public:
FeatureCompatibilityVersion::updateFeatureCompatibilityVersionDocument(
opCtx, actualVersion, requestedVersion, isFromConfigServer);
+ // If the 'useSecondaryDelaySecs' feature flag is disabled in the downgraded FCV, issue
+ // a reconfig to change the 'secondaryDelaySecs' field to 'slaveDelay'.
+ if (isReplSet && repl::feature_flags::gUseSecondaryDelaySecs.isEnabledAndIgnoreFCV() &&
+ requestedVersion < repl::feature_flags::gUseSecondaryDelaySecs.getVersion()) {
+ auto getNewConfig = [&](const repl::ReplSetConfig& oldConfig, long long term) {
+ auto newConfig = oldConfig.getMutable();
+ newConfig.setConfigVersion(newConfig.getConfigVersion() + 1);
+ for (auto mem = oldConfig.membersBegin(); mem != oldConfig.membersEnd();
+ mem++) {
+ newConfig.useSlaveDelayFieldName(mem->getId());
+ }
+
+ return repl::ReplSetConfig(std::move(newConfig));
+ };
+
+ auto status = replCoord->doReplSetReconfig(opCtx, getNewConfig, true /* force */);
+ uassertStatusOKWithContext(status, "Failed to downgrade the replica set config");
+
+ LOGV2(5042303,
+ "Waiting for the downgraded replica set config to propagate to a majority");
+ // If a write concern is given, we'll use its wTimeout. It's kNoTimeout by default.
+ WriteConcernOptions writeConcern(
+ repl::ReplSetConfig::kConfigMajorityWriteConcernModeName,
+ WriteConcernOptions::SyncMode::NONE,
+ opCtx->getWriteConcern().wTimeout);
+ writeConcern.checkCondition = WriteConcernOptions::CheckCondition::Config;
+ repl::OpTime fakeOpTime(Timestamp(1, 1), replCoord->getTerm());
+ uassertStatusOKWithContext(
+ replCoord->awaitReplication(opCtx, fakeOpTime, writeConcern).status,
+ "Failed to wait for the downgraded replica set config to propagate to a "
+ "majority");
+ LOGV2(5042304,
+ "The downgraded replica set config has been propagated to a majority");
+ }
+
{
// Take the global lock in S mode to create a barrier for operations taking the
// global IX or X locks. This ensures that either
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index f8d16936ad9..ce657d65aad 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -853,6 +853,7 @@ env.Library(
'$BUILD_DIR/mongo/client/connection_string',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/server_options_core',
+ '$BUILD_DIR/mongo/idl/feature_flag',
'$BUILD_DIR/mongo/rpc/command_status',
'$BUILD_DIR/mongo/rpc/metadata',
'$BUILD_DIR/mongo/transport/transport_layer_common',
diff --git a/src/mongo/db/repl/member_config.cpp b/src/mongo/db/repl/member_config.cpp
index 7e962d57267..6569a8f73b9 100644
--- a/src/mongo/db/repl/member_config.cpp
+++ b/src/mongo/db/repl/member_config.cpp
@@ -91,7 +91,7 @@ MemberConfig::MemberConfig(const BSONObj& mcfg) {
if (!isVoter()) {
uasserted(ErrorCodes::BadValue, "priority must be 0 when non-voting (votes:0)");
}
- if (getSlaveDelay() > Seconds(0)) {
+ if (getSecondaryDelay() > Seconds(0)) {
uasserted(ErrorCodes::BadValue, "priority must be 0 when slaveDelay is used");
}
if (isHidden()) {
@@ -211,7 +211,13 @@ BSONObj MemberConfig::toBSON(bool omitNewlyAddedField) const {
_splitHorizon.toBSON(configBuilder);
- configBuilder.append(kSlaveDelaySecsFieldName, getSlaveDelaySecs());
+ if (getSecondaryDelaySecs()) {
+ configBuilder.append(kSecondaryDelaySecsFieldName, getSecondaryDelaySecs().get());
+ }
+ if (getSlaveDelaySecs()) {
+ configBuilder.append(kSlaveDelaySecsFieldName, getSlaveDelaySecs().get());
+ }
+
configBuilder.append(kVotesFieldName, MemberConfigBase::getVotes() ? 1 : 0);
return configBuilder.obj();
}
diff --git a/src/mongo/db/repl/member_config.h b/src/mongo/db/repl/member_config.h
index d6bb75622f3..0122b1f05a0 100644
--- a/src/mongo/db/repl/member_config.h
+++ b/src/mongo/db/repl/member_config.h
@@ -63,6 +63,7 @@ public:
using MemberConfigBase::kIdFieldName;
using MemberConfigBase::kNewlyAddedFieldName;
using MemberConfigBase::kPriorityFieldName;
+ using MemberConfigBase::kSecondaryDelaySecsFieldName;
using MemberConfigBase::kSlaveDelaySecsFieldName;
using MemberConfigBase::kTagsFieldName;
using MemberConfigBase::kVotesFieldName;
@@ -134,8 +135,14 @@ public:
* Gets the amount of time behind the primary that this member will atempt to
* remain. Zero seconds means stay as caught up as possible.
*/
- Seconds getSlaveDelay() const {
- return Seconds(getSlaveDelaySecs());
+ Seconds getSecondaryDelay() const {
+ if (getSecondaryDelaySecs()) {
+ return Seconds(getSecondaryDelaySecs().get());
+ }
+ if (getSlaveDelaySecs()) {
+ return Seconds(getSlaveDelaySecs().get());
+ }
+ return Seconds(0);
}
/**
@@ -161,6 +168,20 @@ public:
}
/**
+ * Returns true if this member has the field 'slaveDelay'.
+ */
+ bool hasSlaveDelay() const {
+ return getSlaveDelaySecs().has_value();
+ }
+
+ /**
+ * Returns true if this member has the field 'secondaryDelaySecs'.
+ */
+ bool hasSecondaryDelaySecs() const {
+ return getSecondaryDelaySecs().has_value();
+ }
+
+ /**
* Returns true if this member is newly added from reconfig. This indicates that this node
* should be treated as non-voting.
*/
diff --git a/src/mongo/db/repl/member_config.idl b/src/mongo/db/repl/member_config.idl
index 9f937c39eb8..4340d8957e9 100644
--- a/src/mongo/db/repl/member_config.idl
+++ b/src/mongo/db/repl/member_config.idl
@@ -95,10 +95,19 @@ structs:
slaveDelay:
cpp_name: slaveDelaySecs
type: safeInt64
- default: 0
+ optional: true
+ validator: { gte: 0, lte: 31622400 }
+ description: "The number of seconds 'behind' the primary that this replica set
+ member should 'lag'. This is a legacy field kept for backwards
+ compatibility, and 5.0+ nodes should use secondaryDelaySecs"
+ secondaryDelaySecs:
+ cpp_name: secondaryDelaySecs
+ type: safeInt64
+ optional: true
validator: { gte: 0, lte: 31622400 }
description: "The number of seconds 'behind' the primary that this replica set
- member should 'lag'"
+ member should 'lag'. Only 5.0+ nodes will use this field,
+ and older nodes will use slaveDelay"
arbiterOnly:
type: safeBool
default: false
diff --git a/src/mongo/db/repl/member_config_test.cpp b/src/mongo/db/repl/member_config_test.cpp
index 00c9fb36765..a9cad26e77c 100644
--- a/src/mongo/db/repl/member_config_test.cpp
+++ b/src/mongo/db/repl/member_config_test.cpp
@@ -49,7 +49,7 @@ TEST(MemberConfig, ParseMinimalMemberConfigAndCheckDefaults) {
ASSERT_EQUALS(MemberId(0), mc.getId());
ASSERT_EQUALS(HostAndPort("localhost", 12345), mc.getHostAndPort());
ASSERT_EQUALS(1.0, mc.getPriority());
- ASSERT_EQUALS(Seconds(0), mc.getSlaveDelay());
+ ASSERT_EQUALS(Seconds(0), mc.getSecondaryDelay());
ASSERT_TRUE(mc.isVoter());
ASSERT_FALSE(mc.isHidden());
ASSERT_FALSE(mc.isArbiter());
@@ -675,7 +675,7 @@ TEST(MemberConfig, ParseSlaveDelay) {
<< "h"
<< "priority" << 0 << "slaveDelay" << 100),
&tagConfig);
- ASSERT_EQUALS(Seconds(100), mc.getSlaveDelay());
+ ASSERT_EQUALS(Seconds(100), mc.getSecondaryDelay());
}
{
ASSERT_THROWS(MemberConfig(BSON("_id" << 0 << "host"
@@ -715,6 +715,54 @@ TEST(MemberConfig, ParseSlaveDelay) {
}
}
+TEST(MemberConfig, ParseSecondaryDelay) {
+ ReplSetTagConfig tagConfig;
+ {
+ MemberConfig mc(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 100),
+ &tagConfig);
+ ASSERT_EQUALS(Seconds(100), mc.getSecondaryDelay());
+ }
+ {
+ ASSERT_THROWS(MemberConfig(BSON("_id" << 0 << "host"
+ << "h"
+ << "secondaryDelaySecs" << 100),
+ &tagConfig),
+ ExceptionFor<ErrorCodes::BadValue>);
+ }
+ {
+ MemberConfig mc(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 0),
+ &tagConfig);
+ }
+ {
+ MemberConfig mc(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 3600 * 10),
+ &tagConfig);
+ }
+ {
+ ASSERT_THROWS_CODE(
+ MemberConfig(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << -1),
+ &tagConfig),
+ AssertionException,
+ 51024);
+ }
+ {
+ ASSERT_THROWS_CODE(
+ MemberConfig(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 3600 * 24 * 400),
+ &tagConfig),
+ AssertionException,
+ 51024);
+ }
+}
+
TEST(MemberConfig, ParseAcceptsAnyNumberSlaveDelay) {
ReplSetTagConfig tagConfig;
{
@@ -722,28 +770,28 @@ TEST(MemberConfig, ParseAcceptsAnyNumberSlaveDelay) {
<< "h"
<< "priority" << 0 << "slaveDelay" << 0),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(0));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
}
{
MemberConfig mc(BSON("_id" << 1 << "host"
<< "h"
<< "priority" << 0 << "slaveDelay" << 0.5),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(0));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
}
{
MemberConfig mc(BSON("_id" << 1 << "host"
<< "h"
<< "priority" << 0 << "slaveDelay" << -0.5),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(0));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
}
{
MemberConfig mc(BSON("_id" << 1 << "host"
<< "h"
<< "priority" << 0 << "slaveDelay" << 1),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(1));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(1));
}
{
ASSERT_THROWS_CODE(MemberConfig(BSON("_id" << 1 << "host"
@@ -758,14 +806,69 @@ TEST(MemberConfig, ParseAcceptsAnyNumberSlaveDelay) {
<< "h"
<< "priority" << 0 << "slaveDelay" << 1.6),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(1));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(1));
}
{
MemberConfig mc(BSON("_id" << 1 << "host"
<< "h"
<< "priority" << 0 << "slaveDelay" << 4),
&tagConfig);
- ASSERT_EQUALS(mc.getSlaveDelay(), Seconds(4));
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(4));
+ }
+}
+
+TEST(MemberConfig, ParseAcceptsAnyNumberSecondaryDelay) {
+ ReplSetTagConfig tagConfig;
+ {
+ MemberConfig mc(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 0),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
+ }
+ {
+ MemberConfig mc(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 0.5),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
+ }
+ {
+ MemberConfig mc(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << -0.5),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(0));
+ }
+ {
+ MemberConfig mc(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 1),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(1));
+ }
+ {
+ ASSERT_THROWS_CODE(
+ MemberConfig(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << -1.5),
+ &tagConfig),
+ AssertionException,
+ 51024);
+ }
+ {
+ MemberConfig mc(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 1.6),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(1));
+ }
+ {
+ MemberConfig mc(BSON("_id" << 1 << "host"
+ << "h"
+ << "priority" << 0 << "secondaryDelaySecs" << 4),
+ &tagConfig);
+ ASSERT_EQUALS(mc.getSecondaryDelay(), Seconds(4));
}
}
@@ -990,6 +1093,15 @@ TEST(MemberConfig, ValidatePriorityAndSlaveDelayRelationship) {
ExceptionFor<ErrorCodes::BadValue>);
}
+TEST(MemberConfig, ValidatePriorityAndSecondaryDelayRelationship) {
+ ReplSetTagConfig tagConfig;
+ ASSERT_THROWS(MemberConfig(BSON("_id" << 0 << "host"
+ << "h"
+ << "priority" << 1 << "secondaryDelaySecs" << 60),
+ &tagConfig),
+ ExceptionFor<ErrorCodes::BadValue>);
+}
+
TEST(MemberConfig, ValidatePriorityAndHiddenRelationship) {
ReplSetTagConfig tagConfig;
{
diff --git a/src/mongo/db/repl/repl_server_parameters.idl b/src/mongo/db/repl/repl_server_parameters.idl
index 0fcbefb7a60..a1c56d2c6f7 100644
--- a/src/mongo/db/repl/repl_server_parameters.idl
+++ b/src/mongo/db/repl/repl_server_parameters.idl
@@ -450,3 +450,9 @@ feature_flags:
When enabled, tenant migration commands are supported.
cpp_varname: feature_flags::gTenantMigrations
default: false
+
+ featureFlagUseSecondaryDelaySecs:
+ description: >-
+ If enabled, use field secondaryDelaySecs instead of slaveDelay in MemberConfig.
+ cpp_varname: feature_flags::gUseSecondaryDelaySecs
+ default: false \ No newline at end of file
diff --git a/src/mongo/db/repl/repl_set_config.cpp b/src/mongo/db/repl/repl_set_config.cpp
index 9fb2c00aea6..c40b7428203 100644
--- a/src/mongo/db/repl/repl_set_config.cpp
+++ b/src/mongo/db/repl/repl_set_config.cpp
@@ -97,6 +97,18 @@ ReplSetConfig ReplSetConfig::parseForInitiate(const BSONObj& cfg, OID newReplica
return result;
}
+void ReplSetConfig::setDefaultDelayFieldForMember(MemberConfig mem) {
+ // We should only be setting the default values if the config has neither delay field set.
+ if (!mem.hasSecondaryDelaySecs() && !mem.hasSlaveDelay()) {
+ if (feature_flags::gUseSecondaryDelaySecs.isEnabled(
+ serverGlobalParams.featureCompatibility)) {
+ useSecondaryDelaySecsFieldName(mem.getId());
+ } else {
+ useSlaveDelayFieldName(mem.getId());
+ }
+ }
+}
+
void ReplSetConfig::_setRequiredFields() {
// The three required fields need to be set to something valid to avoid a potential
// invariant if the uninitialized object is ever used with toBSON().
@@ -144,6 +156,8 @@ Status ReplSetConfig::_initialize(bool forInitiate,
// The const_cast is necessary because "non_const_getter" in the IDL doesn't work for
// arrays.
const_cast<MemberConfig&>(member).addTagInfo(&_tagConfig);
+
+ setDefaultDelayFieldForMember(member);
}
//
@@ -252,6 +266,12 @@ Status ReplSetConfig::validate() const {
}
}
+ if (memberI.hasSlaveDelay() && memberI.hasSecondaryDelaySecs()) {
+ return Status(ErrorCodes::BadValue,
+ "Cannot specify both secondaryDelaySecs and slaveDelay as fields in "
+ "member configuration.");
+ }
+
if (memberI.getHostAndPort().isLocalHost()) {
++localhostCount;
}
@@ -377,10 +397,10 @@ Status ReplSetConfig::validate() const {
"Members in replica set configurations being used for config "
"servers must build indexes");
}
- if (mem->getSlaveDelay() != Seconds(0)) {
+ if (mem->getSecondaryDelay() != Seconds(0)) {
return Status(ErrorCodes::BadValue,
"Members in replica set configurations being used for config "
- "servers cannot have a non-zero slaveDelay");
+ "servers cannot have a non-zero secondaryDelaySecs");
}
}
if (serverGlobalParams.clusterRole != ClusterRole::ConfigServer &&
@@ -709,5 +729,23 @@ void MutableReplSetConfig::removeNewlyAddedFieldForMember(MemberId memberId) {
_findMemberByID(memberId)->setNewlyAdded(boost::none);
}
+void MutableReplSetConfig::useSecondaryDelaySecsFieldName(MemberId memberId) {
+ auto mem = _findMemberByID(memberId);
+ if (mem->hasSecondaryDelaySecs()) {
+ return;
+ }
+ mem->setSecondaryDelaySecs(mem->hasSlaveDelay() ? mem->getSlaveDelaySecs() : 0LL);
+ mem->setSlaveDelaySecs(boost::none);
+}
+
+void MutableReplSetConfig::useSlaveDelayFieldName(MemberId memberId) {
+ auto mem = _findMemberByID(memberId);
+ if (mem->hasSlaveDelay()) {
+ return;
+ }
+ mem->setSlaveDelaySecs(mem->hasSecondaryDelaySecs() ? mem->getSecondaryDelaySecs() : 0LL);
+ mem->setSecondaryDelaySecs(boost::none);
+}
+
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/repl_set_config.h b/src/mongo/db/repl/repl_set_config.h
index 8f2ae9d04be..5148d0fa8ea 100644
--- a/src/mongo/db/repl/repl_set_config.h
+++ b/src/mongo/db/repl/repl_set_config.h
@@ -131,6 +131,20 @@ public:
*/
void removeNewlyAddedFieldForMember(MemberId memberId);
+ /**
+ * Sets the member config's 'secondaryDelaySecs' to the value of 'slaveDelay' and removes the
+ * 'slaveDelay' field entirely. If 'slaveDelay' is not set, then sets 'secondaryDelaySecs' to
+ * the default value.
+ */
+ void useSecondaryDelaySecsFieldName(MemberId memberId);
+
+ /**
+ * Sets the member config's 'slaveDelay' to the value of 'secondaryDelaySecs' and removes the
+ * 'secondaryDelaySecs' field entirely. If 'secondaryDelaySecs' is not set, then sets
+ * 'slaveDelay' to the default value.
+ */
+ void useSlaveDelayFieldName(MemberId memberId);
+
protected:
MutableReplSetConfig() = default;
@@ -218,6 +232,13 @@ public:
static ReplSetConfig parseForInitiate(const BSONObj& cfg, OID newReplicaSetId);
/**
+ * Sets the default delay field name for a member config based on feature compatibility version,
+ * but only if the member config has neither 'secondaryDelaySecs' nor 'slaveDelay' already set.
+ * This function is used when constructing 'ReplSetConfigs' for initiate and reconfig.
+ */
+ void setDefaultDelayFieldForMember(MemberConfig mem);
+
+ /**
* Returns true if this object has been successfully initialized or copied from
* an initialized object.
*/
diff --git a/src/mongo/db/repl/repl_set_config_checks.cpp b/src/mongo/db/repl/repl_set_config_checks.cpp
index b768317f041..20ad4106e2f 100644
--- a/src/mongo/db/repl/repl_set_config_checks.cpp
+++ b/src/mongo/db/repl/repl_set_config_checks.cpp
@@ -34,6 +34,7 @@
#include <algorithm>
#include <iterator>
+#include "mongo/db/repl/repl_server_parameters_gen.h"
#include "mongo/db/repl/repl_set_config.h"
#include "mongo/db/repl/replication_coordinator_external_state.h"
#include "mongo/db/service_context.h"
@@ -98,6 +99,54 @@ Status ensureNoNewlyAddedMembers(const ReplSetConfig& config) {
}
/**
+ * Checks that the current feature compatibility version is compatible with
+ * each member config's delay field name. If the feature flag 'featureFlagUseSecondaryDelaySecs' is
+ * enabled, the nodes must use the 'secondaryDelaySecs' field instead of the 'slaveDelay' field.
+ */
+Status isFCVCompatible(const ReplSetConfig& config) {
+ auto version = serverGlobalParams.featureCompatibility.getVersion();
+ // TODO (SERVER-53354) If we are currently upgrading, we check if the feature flag is enabled
+ // for the target version. We use the generic FCV references here to avoid having to update the
+ // FCV constants used after each continuous release. After release, we should make sure to
+ // remove these references while removing the feature flag.
+ //
+ //(Generic FCV reference): feature flag support
+ if (version == ServerGlobalParams::FeatureCompatibility::kUpgradingFromLastLTSToLatest ||
+ version == ServerGlobalParams::FeatureCompatibility::kUpgradingFromLastContinuousToLatest) {
+ version = ServerGlobalParams::FeatureCompatibility::kLatest;
+ }
+
+ ServerGlobalParams::FeatureCompatibility targetFCV;
+ targetFCV.setVersion(version);
+ bool isEnabled = feature_flags::gUseSecondaryDelaySecs.isEnabled(targetFCV);
+ // We must check that every member config has a valid delay field name.
+ for (auto iter = config.membersBegin(); iter != config.membersEnd(); ++iter) {
+ if ((isEnabled && iter->hasSlaveDelay()) || (!isEnabled && iter->hasSecondaryDelaySecs())) {
+ // TODO (SERVER-53354) If the feature flag is disabled, getVersion() will throw. In this
+ // case, the version should default to kLatest. We use the generic FCV references here
+ // to avoid having to update the FCV constants used after each continuous release. After
+ // release, we should make sure to remove these references while removing the feature
+ // flag.
+ //
+ //(Generic FCV reference): feature flag support
+ auto featureFlagVersion = isEnabled
+ ? FeatureCompatibilityVersionParser::toString(
+ feature_flags::gUseSecondaryDelaySecs.getVersion())
+ : FeatureCompatibilityVersionParser::toString(
+ ServerGlobalParams::FeatureCompatibility::kLatest);
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "If the node is in FCV " << featureFlagVersion
+ << ", we must use the secondaryDelaySecs field name "
+ "instead of slaveDelay. Only nodes below FCV "
+ << featureFlagVersion << " should use slaveDelay.");
+ }
+ }
+
+
+ return Status::OK();
+}
+
+/**
* Compares two initialized and validated replica set configurations and checks to see if the
* transition from 'oldConfig' to 'newConfig' adds or removes at most 1 voting node.
*
@@ -331,6 +380,11 @@ StatusWith<int> validateConfigForInitiate(ReplicationCoordinatorExternalState* e
return StatusWith<int>(status);
}
+ status = isFCVCompatible(newConfig);
+ if (!status.isOK()) {
+ return StatusWith<int>(status);
+ }
+
status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern());
if (!status.isOK()) {
return status.withContext(
@@ -370,6 +424,11 @@ Status validateConfigForReconfig(const ReplSetConfig& oldConfig,
return status;
}
+ status = isFCVCompatible(newConfig);
+ if (!status.isOK()) {
+ return status;
+ }
+
status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern());
if (!status.isOK()) {
return status.withContext(
diff --git a/src/mongo/db/repl/repl_set_config_test.cpp b/src/mongo/db/repl/repl_set_config_test.cpp
index 16618f56cbf..2606bd06c42 100644
--- a/src/mongo/db/repl/repl_set_config_test.cpp
+++ b/src/mongo/db/repl/repl_set_config_test.cpp
@@ -820,6 +820,23 @@ TEST(ReplSetConfig, ValidateFailsWithDuplicateMemberId) {
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
+TEST(ReplSetConfig, ValidateFailsWithBothDelaySecsFieldNames) {
+ ReplSetConfig config(ReplSetConfig::parse(
+ BSON("_id"
+ << "rs0"
+ << "protocolVersion" << 1 << "version" << 1 << "configsvr" << true << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "localhost:12345")
+ << BSON("_id" << 1 << "host"
+ << "localhost:54321"
+ << "priority" << 0 << "secondaryDelaySecs" << 10
+ << "slaveDelay" << 10)))));
+ Status status = config.validate();
+ ASSERT_EQUALS(ErrorCodes::BadValue, status);
+ ASSERT_STRING_CONTAINS(status.reason(),
+ "Cannot specify both secondaryDelaySecs and slaveDelay");
+}
+
TEST(ReplSetConfig, InitializeFailsWithInvalidMember) {
ReplSetConfig config;
ASSERT_THROWS(ReplSetConfig::parse(BSON("_id"
@@ -1255,7 +1272,7 @@ bool operator==(const MemberConfig& a, const MemberConfig& b) {
}
}
return a.getId() == b.getId() && a.getHostAndPort() == b.getHostAndPort() &&
- a.getPriority() == b.getPriority() && a.getSlaveDelay() == b.getSlaveDelay() &&
+ a.getPriority() == b.getPriority() && a.getSecondaryDelay() == b.getSecondaryDelay() &&
a.isVoter() == b.isVoter() && a.isArbiter() == b.isArbiter() &&
a.isNewlyAdded() == b.isNewlyAdded() && a.isHidden() == b.isHidden() &&
a.shouldBuildIndexes() == b.shouldBuildIndexes() && a.getNumTags() == b.getNumTags() &&
@@ -1365,8 +1382,8 @@ TEST(ReplSetConfig, toBSONRoundTripAbilityLarge) {
<< BSON("_id" << 3 << "host"
<< "localhost:3828"
<< "arbiterOnly" << false << "hidden" << true << "buildIndexes"
- << false << "priority" << 0 << "slaveDelay" << 17 << "votes"
- << 0 << "newlyAdded" << true << "tags"
+ << false << "priority" << 0 << "secondaryDelaySecs" << 17
+ << "votes" << 0 << "newlyAdded" << true << "tags"
<< BSON("coast"
<< "east"
<< "ssd"
@@ -1558,21 +1575,20 @@ TEST(ReplSetConfig, CheckConfigServerMustBuildIndexes) {
ASSERT_STRING_CONTAINS(status.reason(), "must build indexes");
}
-TEST(ReplSetConfig, CheckConfigServerCantHaveSlaveDelay) {
+TEST(ReplSetConfig, CheckConfigServerCantHaveSecondaryDelaySecs) {
ReplSetConfig configA;
- configA = ReplSetConfig::parse(BSON("_id"
- << "rs0"
- << "protocolVersion" << 1 << "version" << 1 << "configsvr"
- << true << "members"
- << BSON_ARRAY(BSON("_id" << 0 << "host"
- << "localhost:12345")
- << BSON("_id" << 1 << "host"
- << "localhost:54321"
- << "priority" << 0
- << "slaveDelay" << 3))));
+ configA = ReplSetConfig::parse(
+ BSON("_id"
+ << "rs0"
+ << "protocolVersion" << 1 << "version" << 1 << "configsvr" << true << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "localhost:12345")
+ << BSON("_id" << 1 << "host"
+ << "localhost:54321"
+ << "priority" << 0 << "secondaryDelaySecs" << 3))));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
- ASSERT_STRING_CONTAINS(status.reason(), "cannot have a non-zero slaveDelay");
+ ASSERT_STRING_CONTAINS(status.reason(), "cannot have a non-zero secondaryDelaySecs");
}
TEST(ReplSetConfig, CheckConfigServerMustHaveTrueForWriteConcernMajorityJournalDefault) {
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index cde642c1fe9..fe58a8fc366 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -1041,7 +1041,7 @@ Seconds ReplicationCoordinatorImpl::getSecondaryDelaySecs() const {
// queue of work.
return Seconds(0);
}
- return _rsConfig.getMemberAt(_selfIndex).getSlaveDelay();
+ return _rsConfig.getMemberAt(_selfIndex).getSecondaryDelay();
}
void ReplicationCoordinatorImpl::clearSyncSourceBlacklist() {
diff --git a/src/mongo/db/repl/topology_coordinator.cpp b/src/mongo/db/repl/topology_coordinator.cpp
index e98b19e20f8..e7bf9e67d2e 100644
--- a/src/mongo/db/repl/topology_coordinator.cpp
+++ b/src/mongo/db/repl/topology_coordinator.cpp
@@ -483,13 +483,14 @@ bool TopologyCoordinator::_isEligibleSyncSource(int candidateIndex,
return false;
}
// Candidate must not have a configured delay larger than ours.
- if (_selfConfig().getSlaveDelay() < memberConfig.getSlaveDelay()) {
+ if (_selfConfig().getSecondaryDelay() < memberConfig.getSecondaryDelay()) {
LOGV2_DEBUG(3873111,
2,
- "Cannot select sync source with larger slaveDelay than ours",
+ "Cannot select sync source with larger secondaryDelaySecs than ours",
"syncSourceCandidate"_attr = syncSourceCandidate,
- "syncSourceCandidateSlaveDelay"_attr = memberConfig.getSlaveDelay(),
- "slaveDelay"_attr = _selfConfig().getSlaveDelay());
+ "syncSourceCandidateSecondaryDelaySecs"_attr =
+ memberConfig.getSecondaryDelay(),
+ "secondaryDelaySecs"_attr = _selfConfig().getSecondaryDelay());
return false;
}
}
@@ -2096,7 +2097,7 @@ void TopologyCoordinator::fillHelloForReplSet(std::shared_ptr<HelloResponse> res
invariant(!_rsConfig.members().empty());
for (const auto& member : _rsConfig.members()) {
- if (member.isHidden() || member.getSlaveDelay() > Seconds{0}) {
+ if (member.isHidden() || member.getSecondaryDelay() > Seconds{0}) {
continue;
}
auto hostView = member.getHostAndPort(horizonString);
@@ -2130,8 +2131,8 @@ void TopologyCoordinator::fillHelloForReplSet(std::shared_ptr<HelloResponse> res
} else if (selfConfig.getPriority() == 0) {
response->setIsPassive(true);
}
- if (selfConfig.getSlaveDelay() > Seconds(0)) {
- response->setSecondaryDelaySecs(selfConfig.getSlaveDelay());
+ if (selfConfig.getSecondaryDelay() > Seconds(0)) {
+ response->setSecondaryDelaySecs(selfConfig.getSecondaryDelay());
}
if (selfConfig.isHidden()) {
response->setIsHidden(true);
@@ -3098,8 +3099,8 @@ bool TopologyCoordinator::shouldChangeSyncSourceDueToPingTime(const HostAndPort&
return false;
}
- // If we are configured with slaveDelay, do not re-evaluate our sync source.
- if (_selfIndex == -1 || _selfConfig().getSlaveDelay() > Seconds(0)) {
+ // If we are configured with secondaryDelaySecs, do not re-evaluate our sync source.
+ if (_selfIndex == -1 || _selfConfig().getSecondaryDelay() > Seconds(0)) {
return false;
}
diff --git a/src/mongo/db/repl/topology_coordinator_v1_test.cpp b/src/mongo/db/repl/topology_coordinator_v1_test.cpp
index 83d8af17643..bef6dc6238d 100644
--- a/src/mongo/db/repl/topology_coordinator_v1_test.cpp
+++ b/src/mongo/db/repl/topology_coordinator_v1_test.cpp
@@ -434,7 +434,7 @@ TEST_F(TopoCoordTest, NodeReturnsClosestValidSyncSourceAsSyncSource) {
<< "arbiterOnly" << true)
<< BSON("_id" << 50 << "host"
<< "h5"
- << "slaveDelay" << 1 << "priority" << 0)
+ << "secondaryDelaySecs" << 1 << "priority" << 0)
<< BSON("_id" << 60 << "host"
<< "h6")
<< BSON("_id" << 70 << "host"
@@ -4360,13 +4360,13 @@ TEST_F(TopoCoordTest, DontChangeDueToPingTimeWhenCandidatePingTimeIsMissing) {
ReadPreference::Nearest));
}
-TEST_F(ReevalSyncSourceTest, NoChangeWhenNodeConfiguredWithSlaveDelay) {
+TEST_F(ReevalSyncSourceTest, NoChangeWhenNodeConfiguredWithSecondaryDelaySecs) {
updateConfig(BSON("_id"
<< "rs0"
<< "version" << 5 << "term" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "host1:27017"
- << "slaveDelay" << 1 << "priority" << 0)
+ << "secondaryDelaySecs" << 1 << "priority" << 0)
<< BSON("_id" << 1 << "host"
<< "host2:27017")
<< BSON("_id" << 2 << "host"
@@ -4374,7 +4374,8 @@ TEST_F(ReevalSyncSourceTest, NoChangeWhenNodeConfiguredWithSlaveDelay) {
<< "protocolVersion" << 1),
0);
- // Set up so that without slaveDelay, the node otherwise would have changed sync sources.
+ // Set up so that without secondaryDelaySecs, the node otherwise would have changed sync
+ // sources.
getTopoCoord().setPing_forTest(HostAndPort("host2"), pingTime);
getTopoCoord().setPing_forTest(HostAndPort("host3"), significantlyCloserPingTime);
@@ -4391,7 +4392,7 @@ TEST_F(ReevalSyncSourceTest, NoChangeWhenNodeNotFoundInConfig) {
<< "version" << 5 << "term" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "host1:27017"
- << "slaveDelay" << 1 << "priority" << 0)
+ << "secondaryDelaySecs" << 1 << "priority" << 0)
<< BSON("_id" << 1 << "host"
<< "host2:27017")
<< BSON("_id" << 2 << "host"
@@ -4399,8 +4400,8 @@ TEST_F(ReevalSyncSourceTest, NoChangeWhenNodeNotFoundInConfig) {
<< "protocolVersion" << 1),
-1);
- // Set up so that without slaveDelay and not being in the config, the node otherwise would have
- // changed sync sources.
+ // Set up so that without secondaryDelaySecs and not being in the config, the node otherwise
+ // would have changed sync sources.
getTopoCoord().setPing_forTest(HostAndPort("host2"), pingTime);
getTopoCoord().setPing_forTest(HostAndPort("host3"), significantlyCloserPingTime);
diff --git a/src/mongo/dbtests/mock/mock_replica_set.cpp b/src/mongo/dbtests/mock/mock_replica_set.cpp
index d7dfb75aec1..53fd2d0a8cd 100644
--- a/src/mongo/dbtests/mock/mock_replica_set.cpp
+++ b/src/mongo/dbtests/mock/mock_replica_set.cpp
@@ -248,9 +248,9 @@ void MockReplicaSet::mockIsMasterCmd() {
builder.append("passive", true);
}
- if (member->getSlaveDelay().count()) {
- builder.appendIntOrLL("slaveDelay",
- durationCount<Seconds>(member->getSlaveDelay()));
+ if (member->getSecondaryDelay().count()) {
+ builder.appendIntOrLL("secondaryDelaySecs",
+ durationCount<Seconds>(member->getSecondaryDelay()));
}
if (member->isHidden()) {