SERVER-105426 Feature flag checks should default to using lastLTS FCV when the FCV is uninitialized (#40317)

GitOrigin-RevId: 892aaae0e10cb983ff69ed902c54fede2a56a784
This commit is contained in:
Gil Alon 2025-09-02 13:29:17 -04:00 committed by MongoDB Bot
parent cc000571a2
commit f77e78090f
3 changed files with 101 additions and 6 deletions

View File

@ -460,12 +460,16 @@ void ExpressionContext::throwIfFeatureFlagIsNotEnabledOnFCV(
// should check the lowest FCV. We are guaranteed that maxFeatureCompatibilityVersion will
// always be greater than or equal to the last LTS. So we will check the last LTS.
const auto fcv = serverGlobalParams.featureCompatibility.acquireFCVSnapshot();
mongo::multiversion::FeatureCompatibilityVersion versionToCheck = fcv.getVersion();
if (!fcv.isVersionInitialized()) {
// (Generic FCV reference): This FCV reference should exist across LTS binary versions.
versionToCheck = multiversion::GenericFCV::kLastLTS;
} else if (maxFeatureCompatibilityVersion) {
versionToCheck = *maxFeatureCompatibilityVersion;
// (Generic FCV reference): This FCV reference should exist across LTS binary
// versions.
mongo::multiversion::FeatureCompatibilityVersion versionToCheck =
multiversion::GenericFCV::kLastLTS;
// Ensure 'fcv' is initialized before calling 'getVersion()' to prevent an invariant
// error.
if (fcv.isVersionInitialized()) {
versionToCheck =
maxFeatureCompatibilityVersion ? *maxFeatureCompatibilityVersion : fcv.getVersion();
}
uassert(ErrorCodes::QueryFeatureNotAllowed,

View File

@ -33,6 +33,7 @@
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/timestamp.h"
#include "mongo/db/feature_flag_test_gen.h"
#include "mongo/db/logical_time.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/pipeline/expression_context.h"
@ -237,5 +238,66 @@ TEST_F(ExpressionContextTest, DontInitializeUnreferencedVariables) {
ASSERT_FALSE(expCtx->variables.hasValue(Variables::kUserRolesId));
}
TEST_F(ExpressionContextTest, UseLastLTSFeatureFlagWhenFCVUninitialized) {
auto opCtx = makeOperationContext();
std::vector<BSONObj> pipeline;
pipeline.push_back(BSON("$match" << BSON("a" << 1)));
AggregateCommandRequest acr({} /*nss*/, pipeline);
ResolvedNamespaceMap sm;
auto expCtx = make_intrusive<ExpressionContext>(opCtx.get(),
acr,
nullptr /*collator*/,
nullptr /*mongoProcessInterface*/,
sm,
boost::none /*collUUID*/);
const auto kOriginalFCV =
serverGlobalParams.featureCompatibility.acquireFCVSnapshot().getVersion();
ON_BLOCK_EXIT([&] { serverGlobalParams.mutableFCV.setVersion(kOriginalFCV); });
// (Generic FCV reference): for testing only. This comment is required by linter.
serverGlobalParams.mutableFCV.setVersion(
multiversion::FeatureCompatibilityVersion::kUnsetDefaultLastLTSBehavior);
// If the FCV is uninitialized, we should check the feature flag is enabled on the lastLTS
// version.
// 'featureFlagSpoon' should be enabled, since it is a test feature flag that is always enabled
// on the lastLTS version.
ASSERT_DOES_NOT_THROW(
expCtx->throwIfFeatureFlagIsNotEnabledOnFCV("foo", feature_flags::gFeatureFlagSpoon));
// 'featureFlagBlender' should not be enabled, since it is a test feature flag that is always
// enabled on the latest version.
ASSERT_THROWS_CODE(
expCtx->throwIfFeatureFlagIsNotEnabledOnFCV("foo", feature_flags::gFeatureFlagBlender),
DBException,
ErrorCodes::QueryFeatureNotAllowed);
}
TEST_F(ExpressionContextTest, UseLatestFeatureFlagWhenFCVInitialized) {
auto opCtx = makeOperationContext();
std::vector<BSONObj> pipeline;
pipeline.push_back(BSON("$match" << BSON("a" << 1)));
AggregateCommandRequest acr({} /*nss*/, pipeline);
ResolvedNamespaceMap sm;
auto expCtx = make_intrusive<ExpressionContext>(opCtx.get(),
acr,
nullptr /*collator*/,
nullptr /*mongoProcessInterface*/,
sm,
boost::none /*collUUID*/);
const auto& fcvSnapshot = serverGlobalParams.featureCompatibility.acquireFCVSnapshot();
ASSERT_TRUE(fcvSnapshot.isVersionInitialized());
// (Generic FCV reference): for testing only. This comment is required by linter.
ASSERT_TRUE(fcvSnapshot.isGreaterThanOrEqualTo(multiversion::GenericFCV::kLatest));
// Since the FCV is initialized and is the latest version, 'featureFlagSpoon', enabled on the
// lastLTS, and 'featureFlagBlender', enabled on the latest version, should both be enabled.
ASSERT_DOES_NOT_THROW(
expCtx->throwIfFeatureFlagIsNotEnabledOnFCV("foo", feature_flags::gFeatureFlagSpoon));
ASSERT_DOES_NOT_THROW(
expCtx->throwIfFeatureFlagIsNotEnabledOnFCV("foo", feature_flags::gFeatureFlagBlender));
}
} // namespace
} // namespace mongo

View File

@ -708,6 +708,35 @@ TEST(SetupOptions, NonNumericSlowMsYAMLConfigOptionFailsToParse) {
ASSERT_NOT_OK(parser.run(options, argv, &environment));
}
TEST(SetupOptions, ProfileFilterDependsOnFeatureParsesSuccessfully) {
OptionsParserTester parser;
moe::Environment environment;
moe::OptionSection options;
ASSERT_OK(::mongo::addGeneralServerOptions(&options));
ASSERT_OK(::mongo::addNonGeneralServerOptions(&options));
std::vector<std::string> argv;
argv.push_back("binaryname");
argv.push_back("--config");
argv.push_back("config.yaml");
// $toUUID depends on 'featureFlagBinDataConvert' under FCV 8.0. This filter should
// successfully parse, even though we do not verify feature flags in any of these functions.
auto filterObj = BSON("$expr" << BSON("$lt" << BSON_ARRAY(BSON("$toUUID" << 0) << 0.01)));
parser.setConfig("config.yaml",
"operationProfiling:\n"
" filter: '{$expr: {$lt: [{$toUUID: 0}, 0.01]}}'\n");
ASSERT_OK(parser.run(options, argv, &environment));
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
ASSERT_OK(::mongo::setupServerOptions(argv));
ASSERT_OK(::mongo::storeServerOptions(environment));
ASSERT_BSONOBJ_EQ(::mongo::serverGlobalParams.defaultProfileFilter.get(), filterObj);
}
TEST(SetupOptions, SampleRateCommandLineParamParsesSuccessfully) {
OptionsParserTester parser;
moe::Environment environment;