SERVER-119504 Create a skeleton implementation for FCV upgrade/downgrade plugin system (#48466)

GitOrigin-RevId: 8c2d4d7e6aa2b901c0e993c1bad0139870c34f52
This commit is contained in:
marcobizzarri-mdb 2026-03-03 12:33:33 +00:00 committed by MongoDB Bot
parent bfd5cd011a
commit 4a6b93ba10
9 changed files with 780 additions and 0 deletions

View File

@ -389,6 +389,9 @@ fcv:
- src/mongo/db/commands/set_feature_compatibility_version*
- src/mongo/db/op_observer/fcv_op_observer*
- src/mongo/unittest/ensure_fcv.h
- src/mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.*
- src/mongo/db/commands/set_feature_compatibility_version_steps/fcv_step_test.*
- src/mongo/db/commands/set_feature_compatibility_version_steps/legacy_fcv_step.*
access_control:
meta:

View File

@ -1031,6 +1031,7 @@ mongo_cc_library(
"//src/mongo/db/auth:user",
"//src/mongo/db/auth:user_document_parser",
"//src/mongo/db/commands/query_cmd:map_reduce_agg",
"//src/mongo/db/commands/set_feature_compatibility_version_steps",
"//src/mongo/db/global_catalog/ddl:sharding_catalog_manager",
"//src/mongo/db/index_builds:index_builds_coordinator",
"//src/mongo/db/pipeline",

View File

@ -99,6 +99,9 @@ filters:
- "*set_feature_compatibility_version*":
approvers:
- 10gen/server-catalog-and-routing-routing-and-topology
- "*fcv_step*":
approvers:
- 10gen/server-catalog-and-routing-routing-and-topology
- "snapshot_management*":
approvers:
- 10gen/server-replication-reviewers

View File

@ -46,6 +46,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/feature_compatibility_version.h"
#include "mongo/db/commands/set_feature_compatibility_version_gen.h"
#include "mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.h"
#include "mongo/db/database_name.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
@ -1138,6 +1139,9 @@ private:
uasserted(ErrorCodes::CannotUpgrade,
"Simulated dry-run validation failure via fail point.");
}
FCVStepRegistry::get(opCtx->getServiceContext())
.userCollectionsUassertsForUpgrade(opCtx, originalVersion, requestedVersion);
}
// This helper function is for any actions that should be done before taking the global lock in
@ -1149,6 +1153,10 @@ private:
const multiversion::FeatureCompatibilityVersion requestedVersion,
boost::optional<Timestamp> changeTimestamp) {
auto role = ShardingState::get(opCtx)->pollClusterRole();
const auto originalVersion =
getTransitionFCVInfo(
serverGlobalParams.featureCompatibility.acquireFCVSnapshot().getVersion())
.from;
// Note the config server is also considered a shard, so the ConfigServer and ShardServer
// roles aren't mutually exclusive.
if (role && role->has(ClusterRole::ConfigServer)) {
@ -1158,6 +1166,9 @@ private:
if (role && role->has(ClusterRole::ShardServer)) {
// Shard server role actions.
}
FCVStepRegistry::get(opCtx->getServiceContext())
.prepareToUpgradeActionsBeforeGlobalLock(opCtx, originalVersion, requestedVersion);
}
// This helper function is for any user collections creations, changes or deletions that need
@ -1186,6 +1197,9 @@ private:
return collection->shouldRemoveLegacyTimeseriesBucketingParametersHaveChanged();
});
}
FCVStepRegistry::get(opCtx->getServiceContext())
.userCollectionsWorkForUpgrade(opCtx, originalVersion, requestedVersion);
}
// This helper function is for updating server metadata to make sure the new features in the
@ -1213,6 +1227,9 @@ private:
}
_cleanUpDeprecatedCatalogMetadata(opCtx);
FCVStepRegistry::get(opCtx->getServiceContext())
.upgradeServerMetadata(opCtx, originalVersion, requestedVersion);
}
// TODO(SERVER-100328): remove after 9.0 is branched.
@ -1356,6 +1373,11 @@ private:
// This helper function is for any actions that should be done before taking the global lock in
// S mode.
void _prepareToDowngradeActions(OperationContext* opCtx, const FCV requestedVersion) {
const auto originalVersion =
getTransitionFCVInfo(
serverGlobalParams.featureCompatibility.acquireFCVSnapshot().getVersion())
.from;
auto role = ShardingState::get(opCtx)->pollClusterRole();
// Note the config server is also considered a shard, so the ConfigServer and ShardServer
// roles aren't mutually exclusive.
@ -1366,6 +1388,9 @@ private:
if (role && role->has(ClusterRole::ShardServer)) {
// Shard server role actions.
}
FCVStepRegistry::get(opCtx->getServiceContext())
.prepareToDowngradeActions(opCtx, originalVersion, requestedVersion);
}
/**
@ -1573,6 +1598,9 @@ private:
});
}
}
FCVStepRegistry::get(opCtx->getServiceContext())
.userCollectionsUassertsForDowngrade(opCtx, originalVersion, requestedVersion);
}
// Remove cluster parameters from the clusterParameters collections which are not enabled on
@ -1722,6 +1750,9 @@ private:
if (role && role->has(ClusterRole::ShardServer)) {
abortAllMultiUpdateCoordinators(opCtx, requestedVersion, originalVersion);
}
FCVStepRegistry::get(opCtx->getServiceContext())
.internalServerCleanupForDowngrade(opCtx, originalVersion, requestedVersion);
}
void abortAllMultiUpdateCoordinators(OperationContext* opCtx,
@ -2023,6 +2054,8 @@ private:
.migrateRepresentativeQueriesFromQuerySettingsClusterParameterToDedicatedCollection(
opCtx);
}
FCVStepRegistry::get(opCtx->getServiceContext()).finalizeUpgrade(opCtx, requestedVersion);
}
// _finalizeDowngrade is analogous to _finalizeUpgrade, but runs on downgrade. As with
@ -2072,6 +2105,8 @@ private:
opCtx);
service.dropQueryShapeRepresentativeQueriesCollection(opCtx);
}
FCVStepRegistry::get(opCtx->getServiceContext()).finalizeDowngrade(opCtx, requestedVersion);
}
void _forwardDryRunRequestToShards(OperationContext* opCtx,
const SetFeatureCompatibilityVersion& request) {

View File

@ -0,0 +1,34 @@
load("//bazel:mongo_src_rules.bzl", "idl_generator", "mongo_cc_library", "mongo_cc_unit_test")
package(default_visibility = ["//visibility:public"])
exports_files(
glob([
"*.h",
"*.cpp",
]),
)
mongo_cc_library(
name = "set_feature_compatibility_version_steps",
srcs = [
"fcv_step.cpp",
"legacy_fcv_step.cpp",
],
deps = [
"//src/mongo:base",
"//src/mongo/db:service_context",
],
)
mongo_cc_unit_test(
name = "fcv_step_test",
srcs = [
"fcv_step_test.cpp",
],
tags = ["mongo_unittest_third_group"],
deps = [
"//src/mongo/db:service_context_test_fixture",
"//src/mongo/db/commands/set_feature_compatibility_version_steps",
],
)

View File

@ -0,0 +1,139 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.h"
#include "mongo/logv2/log.h"
#include <algorithm>
#include <vector>
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault
namespace mongo {
namespace {
auto fcvStepRegistryDecoration = ServiceContext::declareDecoration<FCVStepRegistry>();
} // namespace
FCVStepRegistry& FCVStepRegistry::get(ServiceContext* serviceContext) {
return fcvStepRegistryDecoration(serviceContext);
}
FCVStepRegistry::~FCVStepRegistry() {
invariant(_steps.empty());
}
void FCVStepRegistry::prepareToUpgradeActionsBeforeGlobalLock(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->prepareToUpgradeActionsBeforeGlobalLock(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::userCollectionsUassertsForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->userCollectionsUassertsForUpgrade(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::userCollectionsWorkForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->userCollectionsWorkForUpgrade(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::upgradeServerMetadata(OperationContext* opCtx,
const FCV originalVersion,
const FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->upgradeServerMetadata(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::finalizeUpgrade(OperationContext* opCtx, const FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->finalizeUpgrade(opCtx, requestedVersion);
});
}
void FCVStepRegistry::prepareToDowngradeActions(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->prepareToDowngradeActions(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::userCollectionsUassertsForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->userCollectionsUassertsForDowngrade(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::internalServerCleanupForDowngrade(OperationContext* opCtx,
const FCV originalVersion,
const FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->internalServerCleanupForDowngrade(opCtx, originalVersion, requestedVersion);
});
}
void FCVStepRegistry::finalizeDowngrade(OperationContext* opCtx, const FCV requestedVersion) {
std::for_each(_steps.begin(), _steps.end(), [&](FCVStep* step) {
step->finalizeDowngrade(opCtx, requestedVersion);
});
}
void FCVStepRegistry::_registerFeature(FCVStep* step) {
_steps.push_back(step);
}
void FCVStepRegistry::_unregisterFeature(FCVStep* step) {
auto it = std::find(_steps.begin(), _steps.end(), step);
invariant(it != _steps.end());
_steps.erase(it);
}
} // namespace mongo

View File

@ -0,0 +1,240 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/db/operation_context.h"
#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
#include "mongo/db/topology/cluster_role.h"
#include "mongo/util/modules.h"
#include "mongo/util/version/releases.h"
#include <string>
#include <utility>
#include <vector>
#include <boost/optional/optional.hpp>
namespace mongo {
/**
* FCVSteps are the steps which need to be executed
* when FCV is upgraded/downgraded.
* Using this interface avoids the need to manually hook the various places in
* SetFeatureCompatibilityVersionCommand where the upgrade/downgrade steps are called.
*
* To define an FCV step, a class need to:
*
* 1. Inherit from FCVStep
* 2. Implement the virtual methods in FCVStep (empty implementations are provided where it makes
* sense)
* 3. Store a singleton object of the class somewhere (ideally as a ServiceContext decoration).
* 4. Define a public static `get(ServiceContext*)` function.
* 5. Define a static FCVStepRegistry::Registerer object to declare the name of the step.
*
* Example:
*
* #include "mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.h"
*
* class FooStep : public FCVStep {
* public:
* static FooStep* get(ServiceContext* serviceContext);
*
* // ...
*
* private:
*
* // Mandatory:
* void userCollectionsUassertsForUpgrade(OperationContext* opCtx,
* FCV originalVersion,
* FCV requestedVersion) {
* ...
* }
*
* void userCollectionsWorkForUpgrade(OperationContext* opCtx,
* FCV originalVersion,
* FCV requestedVersion) {
* ...
* }
*
*
* namespace {
*
* const auto _fooStepDecoration = ServiceContext::declareDecoration<FooStep>();
*
* const FCVStepRegistry::Registerer<FooStep> _FooStepRegisterer("FooStep");
*
* } // namespace
*
* FooStep* FooStep::get(ServiceContext* serviceContext) {
* return &_fooStepDecoration(serviceContext);
* }
*/
/**
* Main API implemented by each FCV step
*/
class MONGO_MOD_PRIVATE FCVStep {
public:
using FCV = multiversion::FeatureCompatibilityVersion;
virtual void prepareToUpgradeActionsBeforeGlobalLock(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void userCollectionsUassertsForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void userCollectionsWorkForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void upgradeServerMetadata(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void finalizeUpgrade(OperationContext* opCtx, FCV requestedVersion) {}
virtual void prepareToDowngradeActions(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void userCollectionsUassertsForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void internalServerCleanupForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) {}
virtual void finalizeDowngrade(OperationContext* opCtx, FCV requestedVersion) {}
/**
* Returns the name of the step. Used for logging purposes.
*/
virtual std::string getStepName() const = 0;
/*
* Allows a step not to register
*/
virtual bool shouldRegisterFCVStep() const {
return true;
}
};
/**
* The registry of FCVSteps.
*/
class MONGO_MOD_PUB FCVStepRegistry final : public FCVStep {
FCVStepRegistry(const FCVStepRegistry&) = delete;
FCVStepRegistry& operator=(const FCVStepRegistry&) = delete;
public:
template <class ActualStep>
class Registerer {
Registerer(const Registerer&) = delete;
Registerer& operator=(const Registerer&) = delete;
public:
explicit Registerer(std::string name)
: _registerer(
std::move(name),
[&](ServiceContext* serviceContext) {
if (!_registered) {
_registered = ActualStep::get(serviceContext)->shouldRegisterFCVStep();
}
if (*_registered) {
FCVStepRegistry::get(serviceContext)
._registerFeature(ActualStep::get(serviceContext));
}
},
[&](ServiceContext* serviceContext) {
if (_registered && *_registered) {
FCVStepRegistry::get(serviceContext)
._unregisterFeature(ActualStep::get(serviceContext));
}
}) {}
private:
boost::optional<bool> _registered;
ServiceContext::ConstructorActionRegisterer _registerer;
};
FCVStepRegistry() = default;
virtual ~FCVStepRegistry();
static FCVStepRegistry& get(ServiceContext* serviceContext);
void prepareToUpgradeActionsBeforeGlobalLock(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void userCollectionsUassertsForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void userCollectionsWorkForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void upgradeServerMetadata(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void finalizeUpgrade(OperationContext* opCtx, FCV requestedVersion) final;
void prepareToDowngradeActions(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void userCollectionsUassertsForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void internalServerCleanupForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final;
void finalizeDowngrade(OperationContext* opCtx, FCV requestedVersion) final;
inline std::string getStepName() const final {
return "FCVStepRegistry";
}
private:
void _registerFeature(FCVStep* step);
void _unregisterFeature(FCVStep* step);
std::vector<FCVStep*> _steps;
};
} // namespace mongo

View File

@ -0,0 +1,233 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.h"
#include "mongo/db/service_context.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/version/releases.h"
namespace mongo {
namespace {
template <class ActualStep>
class TestStep : public FCVStep {
public:
using FCV = FCVStep::FCV;
int numCallsPrepareToUpgradeActionsBeforeGlobalLock{0};
int numCallsUserCollectionsUassertsForUpgrade{0};
int numCallsUserCollectionsWorkForUpgrade{0};
int numCallsUpgradeServerMetadata{0};
int numCallsFinalizeUpgrade{0};
int numCallsPrepareToDowngradeActions{0};
int numCallsUserCollectionsUassertsForDowngrade{0};
int numCallsInternalServerCleanupForDowngrade{0};
int numCallsFinalizeDowngrade{0};
protected:
void prepareToUpgradeActionsBeforeGlobalLock(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsPrepareToUpgradeActionsBeforeGlobalLock++;
}
void userCollectionsUassertsForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsUserCollectionsUassertsForUpgrade++;
}
void userCollectionsWorkForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsUserCollectionsWorkForUpgrade++;
}
void upgradeServerMetadata(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsUpgradeServerMetadata++;
}
void finalizeUpgrade(OperationContext* optCtx, FCV requestedVersion) override {
numCallsFinalizeUpgrade++;
}
void prepareToDowngradeActions(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsPrepareToDowngradeActions++;
}
void userCollectionsUassertsForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsUserCollectionsUassertsForDowngrade++;
}
void internalServerCleanupForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) override {
numCallsInternalServerCleanupForDowngrade++;
}
void finalizeDowngrade(OperationContext* opCtx, FCV requestedVersion) override {
numCallsFinalizeDowngrade++;
}
};
/**
* An FCV Step which is never executed
*/
class FCVStepNeverExecutes : public TestStep<FCVStepNeverExecutes> {
public:
static FCVStepNeverExecutes* get(ServiceContext* serviceContext);
bool shouldRegisterFCVStep() const final {
return false;
}
private:
std::string getStepName() const final {
return "FCVStepNeverExecutes";
}
};
const auto getFCVStepNeverExecutes = ServiceContext::declareDecoration<FCVStepNeverExecutes>();
FCVStepRegistry::Registerer<FCVStepNeverExecutes> fcvStepNeverExecutesRegisterer(
"FCVStepNeverExecutes");
FCVStepNeverExecutes* FCVStepNeverExecutes::get(ServiceContext* serviceContext) {
return &getFCVStepNeverExecutes(serviceContext);
}
/**
* An FCV Step which is always executed
*/
class FCVStepAlwaysExecutes : public TestStep<FCVStepAlwaysExecutes> {
public:
static FCVStepAlwaysExecutes* get(ServiceContext* serviceContext);
bool shouldRegisterFCVStep() const final {
return true;
}
private:
std::string getStepName() const final {
return "FCVStepAlwaysExecutes";
}
};
const auto getFCVStepAlwaysExecutes = ServiceContext::declareDecoration<FCVStepAlwaysExecutes>();
FCVStepRegistry::Registerer<FCVStepAlwaysExecutes> fcvStepAlwaysExecutesRegister(
"FCVStepAlwaysExecutes");
FCVStepAlwaysExecutes* FCVStepAlwaysExecutes::get(ServiceContext* serviceContext) {
return &getFCVStepAlwaysExecutes(serviceContext);
}
class FCVStepRegistryTest : public ServiceContextTest {};
TEST_F(FCVStepRegistryTest, FCVStepRegistrySimple) {
auto sc = getGlobalServiceContext();
auto opCtxHolder = makeOperationContext();
auto opCtx = opCtxHolder.get();
auto a = FCVStepNeverExecutes::get(sc);
auto b = FCVStepAlwaysExecutes::get(sc);
auto originalVersion = FCVStep::FCV::kVersion_8_0;
auto requestedVersion = FCVStep::FCV::kVersion_8_3;
ASSERT_EQ(0, a->numCallsPrepareToUpgradeActionsBeforeGlobalLock);
ASSERT_EQ(0, a->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, a->numCallsUserCollectionsWorkForUpgrade);
ASSERT_EQ(0, a->numCallsUpgradeServerMetadata);
ASSERT_EQ(0, a->numCallsFinalizeUpgrade);
ASSERT_EQ(0, a->numCallsPrepareToDowngradeActions);
ASSERT_EQ(0, a->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, a->numCallsInternalServerCleanupForDowngrade);
ASSERT_EQ(0, a->numCallsFinalizeDowngrade);
ASSERT_EQ(0, b->numCallsPrepareToUpgradeActionsBeforeGlobalLock);
ASSERT_EQ(0, b->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, b->numCallsUserCollectionsWorkForUpgrade);
ASSERT_EQ(0, b->numCallsUpgradeServerMetadata);
ASSERT_EQ(0, b->numCallsFinalizeUpgrade);
ASSERT_EQ(0, b->numCallsPrepareToDowngradeActions);
ASSERT_EQ(0, b->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, b->numCallsInternalServerCleanupForDowngrade);
ASSERT_EQ(0, b->numCallsFinalizeDowngrade);
FCVStepRegistry::get(sc).prepareToUpgradeActionsBeforeGlobalLock(
opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).userCollectionsUassertsForUpgrade(
opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).userCollectionsWorkForUpgrade(
opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).upgradeServerMetadata(opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).finalizeUpgrade(opCtx, requestedVersion);
FCVStepRegistry::get(sc).prepareToDowngradeActions(opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).userCollectionsUassertsForDowngrade(
opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).internalServerCleanupForDowngrade(
opCtx, originalVersion, requestedVersion);
FCVStepRegistry::get(sc).finalizeDowngrade(opCtx, requestedVersion);
ASSERT_EQ(0, a->numCallsPrepareToUpgradeActionsBeforeGlobalLock);
ASSERT_EQ(0, a->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, a->numCallsUserCollectionsWorkForUpgrade);
ASSERT_EQ(0, a->numCallsUpgradeServerMetadata);
ASSERT_EQ(0, a->numCallsFinalizeUpgrade);
ASSERT_EQ(0, a->numCallsPrepareToDowngradeActions);
ASSERT_EQ(0, a->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(0, a->numCallsInternalServerCleanupForDowngrade);
ASSERT_EQ(0, a->numCallsFinalizeDowngrade);
ASSERT_EQ(1, b->numCallsPrepareToUpgradeActionsBeforeGlobalLock);
ASSERT_EQ(1, b->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(1, b->numCallsUserCollectionsWorkForUpgrade);
ASSERT_EQ(1, b->numCallsUpgradeServerMetadata);
ASSERT_EQ(1, b->numCallsFinalizeUpgrade);
ASSERT_EQ(1, b->numCallsPrepareToDowngradeActions);
ASSERT_EQ(1, b->numCallsUserCollectionsUassertsForDowngrade);
ASSERT_EQ(1, b->numCallsInternalServerCleanupForDowngrade);
ASSERT_EQ(1, b->numCallsFinalizeDowngrade);
}
} // namespace
} // namespace mongo

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/commands/set_feature_compatibility_version_steps/fcv_step.h"
#include "mongo/db/service_context.h"
namespace mongo {
class LegacyFCVStep : public mongo::FCVStep {
public:
static LegacyFCVStep* get(ServiceContext* serviceContext);
inline std::string getStepName() const final {
return "LegacyFCVStep";
}
private:
void prepareToUpgradeActionsBeforeGlobalLock(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void userCollectionsUassertsForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void userCollectionsWorkForUpgrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void upgradeServerMetadata(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void finalizeUpgrade(OperationContext* optCtx, FCV requestedVersion) final {}
void prepareToDowngradeActions(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void userCollectionsUassertsForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void internalServerCleanupForDowngrade(OperationContext* opCtx,
FCV originalVersion,
FCV requestedVersion) final {}
void finalizeDowngrade(OperationContext* opCtx, FCV requestedVersion) final {}
};
namespace {
const auto _sampleDecoration = ServiceContext::declareDecoration<LegacyFCVStep>();
const FCVStepRegistry::Registerer<LegacyFCVStep> _LegacyFCVStepRegisterer("LegacyFCVStep");
} // namespace
LegacyFCVStep* LegacyFCVStep::get(ServiceContext* serviceContext) {
return &_sampleDecoration(serviceContext);
}
} // namespace mongo