MongoDB 多版本Multiversion升级/降级测试体系解析jstests/multiVersion 的架构、FCV 机制与分支维护策略【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo多版本Multiversion测试是 MongoDB 验证跨版本兼容性的核心测试套件它专门检验不同 MongoDB 版本之间预期的升级upgrade与降级downgrade行为。本文以 jstests/multiVersion/README.md 为骨架结合该目录下数百个实际测试文件与辅助库源码系统讲解多版本测试的组织结构、FCV 版本兼容机制、测试编写范式以及分支branching后测试维护的决策策略帮助读者理解并上手 MongoDB 的跨版本兼容测试。一、多版本测试是什么定义与定位jstests/multiVersion/README.md 用一句话定义了整个目录的使命These tests test upgrade/downgrade behavior expected between different versions of MongoDB.即该目录下所有测试专门验证 MongoDB 不同版本之间的升级与降级行为是否符合预期。这包括但不限于从旧版本二进制启动的数据文件能否被新版本二进制正确读取数据文件、索引格式、集合元数据在 FCVFeature Compatibility Version特性兼容版本升级/降级后是否保持一致混合版本副本集mixed-version replica set中主节点、从节点、仲裁节点在不同二进制版本下的复制与选举行为分片集群在混合版本、混合 FCV 状态下的分片迁移、元数据克隆与配置服务器协调行为升级/降级过程中发生写入失败、网络中断、节点崩溃等异常时FCV 状态机能否正确恢复。与普通的单版本功能测试不同多版本测试需要同时操控多个版本的 mongod/mongos 二进制因此仓库为它配套了专门的测试辅助库位于 jstests/multiVersion/libs/例如负责副本集滚动升级的multi_rs.js、负责版本校验的verify_versions.js等。二、测试目录结构总览jstests/multiVersion目录按测试关注面分为四大子目录另有若干顶层测试文件路径内容定位genericBinVersion/通用二进制版本测试仅涉及用不同版本二进制启动、切换的场景不依赖特定功能例如索引格式、集合元数据、启动行为、混合版本 mongod 间迁移等genericChangeStreams/变更流Change Streams在多版本/多 FCV 场景下的行为测试如 FCV 切换触发变更流重定位、降级后读取 oplog 等genericSetFCVUsage/使用setFeatureCompatibilityVersionsetFCV进行升级/降级的测试覆盖副本集、分片集群、时间序列、索引构建等各功能面libs/供上述测试共享的 JavaScript 辅助库其中 genericSetFCVUsage/fcv_core/ 是最核心的基础设施测试目录其 README 说明This folder contains tests the core FCV and setFCV upgrade/downgrade infrastructure. It does not contain tests linked to any other particular feature.即该目录专门测试 FCV 与 setFCV 升级/降级的核心基础设施不绑定任何特定功能是所有多版本测试的地基。而genericBinVersion/下的顶层文件如 example_fcv_upgrade_downgrade_test.js则扮演了教学示例与通用模板的角色。每个子目录都带有自己的 BUILD.bazel 与 OWNERS.yml测试以mongo_js_library规则构建便于在 Bazel 体系内被各测试目标引用。三、核心概念FCV 与动态版本标签多版本测试的运行依赖一套版本标签体系。仓库在 jstests/multiVersion/libs/supported_versions.js 中维护了当前所有受支持的版本该文件注释明确要求随版本发布/淘汰保持更新并留有 TODO SERVER-76166 计划未来以程序化方式生成该列表export const allSupportedVersions [ {binVersion: 7.0, featureCompatibilityVersion: 7.0, testCollection: seven_zero}, {binVersion: 8.0, featureCompatibilityVersion: 8.0, testCollection: eight_zero}, {binVersion: 9.0, featureCompatibilityVersion: 9.0, testCollection: nine_zero}, {binVersion: last-lts, featureCompatibilityVersion: lastLTSFCV, testCollection: last_lts}, { binVersion: last-continuous, featureCompatibilityVersion: lastContinuousFCV, testCollection: last_continuous, }, {binVersion: latest, featureCompatibilityVersion: latestFCV, testCollection: latest}, ];这里体现了 MongoDB 多版本测试的两类版本概念固定版本号如7.0、8.0、9.0指向具体的历史发布版本二进制动态版本标签README 中提到的 last dynamic version features 即指此类latest当前开发分支构建出的最新二进制对应 FCV 为全局变量latestFCVlast-lts上一个长期支持Long Term Support版本对应lastLTSFCVlast-continuous上一个持续发布Continuous版本对应lastContinuousFCV。这些标签会随开发周期推进自动指向新的具体版本因此基于它们编写的测试应该无论 MongoDB 版本如何始终通过——这正是 README 要求对涉及 last 动态版本功能的测试进行健壮性增强的原因。四、编写一个 FCV 升级/降级测试example_fcv_upgrade_downgrade_test.js 深度解析要快速上手多版本测试最直接的入口是 genericBinVersion/example_fcv_upgrade_downgrade_test.js。该文件注释即表明它是展示如何测试某个被 FCV 门控FCV-gated的特性开关的升级/降级行为的示例。其完整流程如下import jstests/multiVersion/libs/multi_rs.js; import {FeatureFlagUtil} from jstests/libs/feature_flag_util.js; import {ReplSetTest} from jstests/libs/replsettest.js; // 以 last-lts 二进制版本启动副本集。 const nodeOption { binVersion: last-lts, }; // 至少需要 2 个节点因为 upgradeSet 方法需要调用 step down // 并需要一个仍可当选主节点的节点。 const replSet new ReplSetTest({nodes: [nodeOption, nodeOption]}); replSet.startSet(); replSet.initiate(null, null, {initiateWithDefaultElectionTimeout: true}); // 升级整个集合的二进制并启用特性开关。 // 特性开关会随 latest FCV 生效但此时副本集 FCV 仍为 last-lts。 replSet.upgradeSet({binVersion: latest, setParameter: {featureFlagToaster: true}}); const primary replSet.getPrimary(); const admin primary.getDB(admin); // 特性开关此时不应生效。 assert(!FeatureFlagUtil.isEnabled(admin, Toaster)); // 任何 pre-FCV-upgradeFCV 升级前的测试应写在这里。 // 升级 FCV并确认特性开关现在已启用。 assert.commandWorked( primary.adminCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}), ); assert(FeatureFlagUtil.isPresentAndEnabled(admin, Toaster)); // 任何 post-FCV-upgradeFCV 升级后的测试应写在这里。 // 降级 FCV并确认特性开关现在已禁用。 assert.commandWorked( primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}), ); assert(!FeatureFlagUtil.isEnabled(admin, Toaster)); // 任何 post-FCV-downgradeFCV 降级后的测试应写在这里。 replSet.stopSet();这个示例揭示了测试一个 FCV 门控功能的标准四阶段模式旧版本二进制启动以last-lts启动副本集二进制先行升级FCV 不动调用replSet.upgradeSet({binVersion: latest, ...})把全部节点二进制换成latest但此时 FCV 仍是last-lts所以被latestFCV 门控的特性开关尚未生效——这验证了新二进制必须兼容旧 FCV的前向兼容性FCV 升级通过setFeatureCompatibilityVersion: latestFCV, confirm: true提升 FCV此时特性开关才真正启用FCV 降级通过setFeatureCompatibilityVersion: lastLTSFCV, confirm: true降回特性开关随之禁用——这验证了降级后新特性必须被正确关闭的回滚安全性。注意confirm: true参数自 MongoDB 引入先进入降级中/升级中中间状态、再确认提交的两阶段 FCV 切换机制后setFCV 命令需要显式确认。关于该状态机的深入测试见下文 fcv_core 部分。五、setFCV 核心基础设施测试fcv_core 目录fcv_core 是整个多版本测试体系的心脏包含 30 余个专门测试 FCV 状态机的用例其中两个代表性文件值得深入5.1 do_upgrade_downgrade.js全场景升级/降级演练do_upgrade_downgrade.js 用统一的先设 FCV、再换二进制先setFeatureCompatibilityVersion后切换binVersion策略覆盖了 standalone、副本集、--shardsvr、--configsvr共四种拓扑 ×last-lts/last-continuous两个降级目标版本的全部组合。其核心验证点包括FCV 初始值与持久化普通 standalone 初始 FCV 为latestFCV而带shardsvr的节点初始为lastLTSFCV从源码注释可见这是 shard 节点的特殊约定数据与索引格式兼容每次 FCV 切换后都调用checkCollectionUUIDs校验所有集合 UUID 完好调用checkUniqueIndexFormatVersion校验唯一索引格式版本并在降级后执行recreateUniqueIndexes把v: 2的唯一索引按旧 FCV 格式v: 1重建从配置服务器发起的特殊转换在last-lts与last-continuous之间切换 FCV 时只有带fromConfigServer: true参数的 setFCV 调用才被允许普通 standalone / 副本集不允许直接在这两个 FCV 间跳转降级后旧二进制可读用旧版本二进制以相同 dbpath 重启确认 FCV 文档、集合 UUID 均保持正确再升级恢复切回latest二进制后再次 setFCV 到latestFCV确认一切恢复原状。该文件在 replica set 场景中还会为每个从节点在local库插入数据、在主节点与从节点上分别重建唯一索引从而确保所有成员而非仅主节点都通过格式兼容检查。5.2 set_feature_compatibility_version.js边界与异常路径set_feature_compatibility_version.js 则系统性地测试 setFCV 命令的所有失败路径与边界条件非法版本值数字、低于lastLTSFCV的旧版本、高于latestFCV的未来版本均必须失败当last-lts与last-continuous之间存在空档时中间版本也不被支持命令合法性setFeatureCompatibilityVersion只能作用于 admin 库、不能通过setParameter设置、拒绝未知字段写入失败回滚通过failCollectionUpdatesfail point 模拟对admin.system.version的写入失败验证 setFCV 失败后 FCV 保持原值不变两阶段状态机升级/降级未完成时再次发起指向其他版本或相反方向的 setFCV 会以错误码5147403失败只有在确认操作完成后才能继续下一次转换写关注超时用stopServerReplication暂停从节点复制使 setFCV 的 majority 写关注超时WriteConcernTimeout验证命令失败而 FCV 停留在中间状态复制与初始同步验证降级二进制从节点能从FCV 已降级的 latest 主节点成功完成初始同步以及 FCV 升级后旧版本从节点不再复制新写入避免其读到无法理解的新格式数据而崩溃幂等性对已处于目标 FCV 的副本集重复 setFCV 必须成功且无副作用。此外该文件在分片集群场景验证了mongos 上的 setFCV 校验、配置服务器与分片之间 FCV 的传播、网络隔离通过discardMessagesFrom桥接导致命令失败时 FCV 停留在降级中状态以及 dry-run 模式SetFcvDryRunMode特性开关下失败不影响 FCV 等行为。六、多版本测试辅助库6.1 multi_rs.js副本集滚动升级引擎multi_rs.js 为ReplSetTest原型注入了upgradeSet、upgradeSecondaries、upgradeArbiters、upgradePrimary、upgradeNode、stepdown等一整套升级原语。以upgradeSet为例其升级顺序严格为upgradeSecondaries先升级所有非仲裁从节点upgradeArbiters再升级仲裁节点——注意对仲裁节点降级到last-lts时不会降级其数据文件而是强制startClean: true重建 dbpath源码注释明确说明不支持为仲裁节点降级数据文件upgradePrimary最后对主节点执行stepdown源码中处理了 stepdown 时主从 OpTime 相同的竞态通过向garbageWriteToAdvanceOpTime写入一条垃圾数据推动从节点追赶等待旧主节点变为从节点后再升级它。upgradeNode在重启前会把节点置为RECOVERING状态replSetMaintenance从而避免其在重启期间参与选举。这套先从后主的滚动升级逻辑正是 MongoDB 官方升级流程在测试框架中的复刻。6.2 verify_versions.js进程版本校验verify_versions.js 提供两个断言工具assert.binVersion(mongo, version)通过serverStatus读取实际运行版本并与期望版本比对使用MongoRunner.areBinVersionsTheSame归一化比较assert.allBinVersions(versionsWanted, versionsFound)逐一确认期望版本列表中的每个版本都真实出现在已启动进程列表中。多版本测试用它们来确认测试确实跑在了想要的版本组合上。6.3 mixed_version_fixture_test.js滚动重启场景模板mixed_version_fixture_test.js 提供了高度可复用的滚动重启测试模板testPerformReplSetRollingRestart以指定版本启动 3 节点副本集后依次执行升级从节点 → 断言 → 升级主节点 → 断言 →可选FCV 升级 → 断言 → FCV 降级 → 断言的完整流程各阶段通过setupFn、beforeRestart、afterSecondariesHaveRestarted、afterPrimariesHaveRestarted、afterFCVBump等回调注入具体断言。基于它还封装了testPerformUpgradeReplSet一键模拟从 last-lts 完整升级到 latest 并最终提升 FCV的标准升级流程非常适合被各功能团队复用。七、分支Branching时的测试维护策略README 的核心决策指南jstests/multiVersion/README.md 除了定义测试目标外还给出了一条针对开发分支branching时刻的运维准则Those that begin failing upon branching should be assessed by the owner teams即一旦发生分支某些多版本测试会开始失败此时应由测试所属的负责人团队owner teams逐一评估并依据两个问题做出处置Is the test only applicable to specific versions during specific development cycles?如果该测试只适用于特定开发周期中的特定版本例如只针对某个过渡版本的行为则应把它从无关的分支和 master 上删除。这类测试的生命周期与具体版本绑定留在其他分支上只会产生与版本无关的噪声失败。Does the test add value for last (dynamic) version features?如果该测试对last-lts/last-continuous/latest这类动态版本特性具有持续价值则应修改测试使其更健壮more robust让这些测试无论 MongoDB 版本如何始终通过。这两条策略可以整理为一张决策表评估问题答案处置动作目标测试仅适用于特定开发周期的特定版本是从无关分支和 master 中删除消除与版本无关的失败噪声测试为 last 动态版本功能增加价值是修改测试以增强健壮性无论 MongoDB 版本如何都稳定通过其背后的工程动机是多版本测试天然与版本窗口耦合。分支之后旧分支上不再存在latest新分支才构建而 master 上last-lts/last-continuous的具体指向也会前移。因此绑定具体版本的测试需要清理绑定动态版本标签的测试则需要保持通用性——这也解释了为什么supported_versions.js会同时维护固定版本号与动态版本标签两类条目。八、如何运行多版本测试多版本测试是标准的 JavaScript 集成测试随仓库的 resmoke 测试框架运行入口见 buildscripts/resmoke.py并通过 Bazel 的mongo_js_library规则纳入构建与测试目标见各子目录的 BUILD.bazel其中all_subpackage_javascript_files()会递归收集所有子目录的 JS 测试文件。运行前需要注意的前提条件需要下载对应版本的 MongoDB 二进制多版本测试通过binVersion标签如latest、last-lts启动不同版本的 mongod/mongos运行环境必须能解析并获取这些二进制仓库配套的 buildscripts/setup_multiversion_mongodb.py 等脚本可用于准备多版本测试所需的二进制集合按测试目录或具体文件选择测试例如fcv_core的用例可单独运行也可连同整个jstests/multiVersion目录的 suite 一起执行失败时按分支策略处置若某用例在分支后开始失败应回到第七节的评估流程判断删除还是增强健壮性。结语jstests/multiVersion是 MongoDB 保障跨版本兼容性的主战场它以 README 定义的升级/降级行为验证为纲领以 FCV 状态机为核心机制以latest/last-lts/last-continuous动态版本标签为兼容窗口以multi_rs.js的滚动升级引擎和mixed_version_fixture_test.js的场景模板为骨架构成了从 standalone 到副本集再到分片集群的全拓扑覆盖。而分支时刻的删除过时测试、增强动态版本测试两条维护准则则保证了这套庞大的测试体系能在每个版本周期中持续、稳定地发挥作用。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考