MongoDB resmoke 测试框架 Fixtures 全解析:拓扑定义、生命周期与实战配置
MongoDB resmoke 测试框架 Fixtures 全解析拓扑定义、生命周期与实战配置【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo导读本文以 MongoDB 官方测试框架 resmoke 中buildscripts/resmokelib/testing/fixtures/目录的 README 为骨架系统讲解「Fixture」这一测试拓扑抽象层的设计思想与全部内置实现——从 standalone mongod、副本集、分片集群到外部集群与 mongot 搜索拓扑并结合仓库源码接口定义、构造器工厂、真实 Suite 配置给出可直接落地的 YAML 配置示例。读完本文你将掌握如何为一个 JSTest Suite 选择、配置与理解 resmoke 的各类拓扑夹具并理解其setup → await_ready → teardown生命周期背后的实现机制。一、什么是 Fixture测试拓扑的抽象层在 resmokeMongoDB 的 JavaScript 测试执行框架中Fixture 定义了测试将要运行于其上的特定拓扑topology——例如单机 mongod、三节点副本集、多分片集群等。官方 README 的第一句话即给出了这一定义Fixtures define a specific topology that tests run against.Fixtures 存在于buildscripts/resmokelib/testing/fixtures/目录下它们通过统一的生命周期接口setup/await_ready/teardown向上层测试执行器屏蔽了进程编排细节一个测试套件只需要声明我要一个副本集剩下的节点启动、端口分配、副本集初始化、主节点选举、数据目录清理都由对应 Fixture 完成。1.1 Fixture 在 Suite 配置中的位置每个 resmoke Suite 是一个 YAML 文件位于 buildscripts/resmokeconfig/suites/ 目录其中通过fixture键声明所使用的拓扑。以 core.yml 为例L40-L47fixture: class: MongoDFixture mongod_options: set_parameters: enableTestCommands: 1 internalQueryStatsSampleRate: 1.0 internalQueryStatsWriteCmdSampleRate: 1.0 internalQueryStatsErrorsAreCommandFatal: trueclass字段指定 Fixture 类名其余键则是该 Fixture 的构造参数如mongod_options、num_nodes、num_shards等。所有可用的class值及其含义正是本文接下来要逐一展开的内容。1.2 统一的构造入口make_fixture 工厂所有 Fixture 并非直接实例化而是经由 _builder.py 中的make_fixture工厂函数创建L44-L71def make_fixture( class_name, logger, job_num, *args, enable_feature_flagsTrue, exclude_ifr_flagsFalse, **kwargs ): fixturelib FixtureLib() if class_name in _BUILDERS: builder _BUILDERS[class_name]() return builder.build_fixture(logger, job_num, fixturelib, *args, **kwargs) if class_name not in _FIXTURES: raise ValueError(Unknown fixture class %s % class_name) ...可以看到构造流程分为两条路径普通路径从注册表_FIXTURES中按类名直接实例化Builder 路径对于需要特殊组装逻辑的复杂拓扑如副本集、分片集群由注册在_BUILDERS中的FixtureBuilder子类负责拼装——这正是多版本multiversion测试得以实现的关键后文 4.2 节详述。类名注册机制来自interface.py中基于registry.make_registry_metaclass(_FIXTURES)的元类L90REGISTERED_NAME即该 Fixture 在 YAML 中可用的class值。二、内置 Fixture 全景类、文件与适用场景README 中共列出了 10 个可直接指定为fixture的类下表汇总后逐一详解Fixture 类class值源文件拓扑说明BulkWriteFixturebulk_write.py为 bulkWrite 测试提供一组集群ExternalFixtureexternal.py连接外部非 resmoke 启动的集群ExternalShardedClusterFixtureshardedcluster.py与外部分片集群交互MongoDFixturestandalone.py单机 mongodMongoTFixturemongot.py与 mongod 伴生的 mongot 搜索进程MultiReplicaSetFixturemulti_replica_set.py一组副本集MultiShardedClusterFixturemulti_sharded_cluster.py一组分片集群ReplicaSetFixturereplicaset.py副本集ShardedClusterFixtureshardedcluster.py分片集群YesFixtureyesfixture.py启动若干yes进程制造海量日志2.1 MongoDFixture单机 mongodMongoDFixture是提供一个独立 mongod 供 JSTest 运行的最基础 Fixture定义于 standalone.pyL32。其构造参数几乎覆盖了 mongod 启动的全部关切点mongod_executable/mongod_options可执行文件路径与启动参数set_parameters、dbpath、port等add_feature_flags是否将所有 Feature Flag 注入set_parameters值为true用于验证新特性开启状态下的行为exclude_ifr_flags则可在注入时排除 Incremental Feature RolloutIFR相关 flag多版本场景下使用L156-L161dbpath_prefix/preserve_dbpath数据目录前缀及是否保留数据默认在setup()时会先删除旧 dbpathL233-L238port/use_priority_port监听端口与 priority port若未显式指定端口由fixturelib.get_next_port(job_num)自动分配L187launch_mongot是否同时启动 mongot搜索相关见 2.5 节load_extensions/skip_extensions_signature_verification启动时加载的扩展[*]表示发现并加载所有*_mongo_extension.so文件也可指定具体名称如[add_fields_match]uds_path_prefixUnix domain socket 目录前缀传入True时使用 mongod 数据目录MongoDB 会创建{unixSocketPrefix}/mongodb-{port}.sock。在await_ready()中该 Fixture 会以RESMOKE_ADMIN_APPNAME为 appName 建立管理连接并发送ping直至收到响应L333-L363。teardown则按TeardownModeTERMINATE15/KILL9/ABORT6对应 SIGTERM/SIGKILL/SIGABRT停止 mongod 并校验退出码。2.2 ReplicaSetFixture副本集ReplicaSetFixture是提供一个副本集供 JSTest 运行的标准选择定义于 replicaset.pyL52。它的参数决定了副本集的形态重点包括num_nodes节点数默认 2可通过全局配置NUM_REPLSET_NODES覆盖start_initial_sync_node/electable_initial_sync_node/hide_initial_sync_node_from_conn_string是否额外启动一个初始同步节点用于测试 initial sync 场景、该节点是否可被选为主、是否从连接串中隐藏all_nodes_electable/voting_secondaries所有节点是否可选举、secondary 是否参与投票replset_config_options透传给replSetInitiate/replSetReconfig的配置项如configsvr: true时即成为 config server 形态linear_chain线性链复制拓扑真实 Suite 示例见 4.1 节write_concern_majority_journal_defaultw:majority 的 journal 默认值use_auto_bootstrap_procedure使用自动引导流程首个节点自动初始化后通过hello返回的setName更新其余节点的--replSet参数L230-L249auth_options、default_read_concern、default_write_concern等。此外该 Fixture 默认将 oplog 大小设为 511MBself.mongod_options.setdefault(oplogSize, 511)L188。它实现了ReplFixture接口的get_primary()/get_secondaries()并提供retry_until_wtimeout辅助方法以写操作回调的方式持续重试连接失败直至AWAIT_REPL_TIMEOUT_MINS5 分钟超时见 interface.py 中 ReplFixture 的实现。2.3 ShardedClusterFixture分片集群ShardedClusterFixture是提供一个分片集群供 JSTest 运行的复杂拓扑定义于 shardedcluster.pyL31。关键构造参数num_shards分片数默认 1num_rs_nodes_per_shard每个分片副本集的节点数默认 1num_mongosmongos 路由器数量默认 1enable_balancer是否启用均衡器configsvr_options/shard_optionsconfig server 与分片的专属选项config_shard配置分片catalog shard编号支持use_auto_bootstrap_procedure时若未指定会自动设为 0见 _builder.py 中 ShardedClusterBuilder._mutate_kwargsshard_replset_name_prefixshard-rs/configsvr_replset_nameconfig-rs分片与 config server 副本集名称replica_set_endpoint、random_migrations、launch_mongot等。从源码结构看分片集群的搭建路径为ShardedClusterBuilder.build_fixture→ 先构建 config serverconfig server 本身就是ReplicaSetFixture通过install_configsvr装入→ 为每个分片构建ReplicaSetFixtureinstall_rs_shard→ 再构建num_mongos个 mongos 路由器L452-L554。分片数据节点会在setup()阶段通过addShard命令逐一分片加入集群。2.4 MultiReplicaSetFixture 与 MultiShardedClusterFixture多集群拓扑这两个 Fixture 都继承自MultiClusterFixture用于同时提供多个相互独立的参与集群——它们各自可以独立工作仅在参与某个过程如数据迁移的特定时段内被绑定在一起README 对MultiClusterFixture的官方描述。定义分别位于 multi_replica_set.pyL10与 multi_sharded_cluster.pyL12。MultiReplicaSetFixture参数num_replica_sets默认 2且要求 ≥ 2、num_nodes_per_replica_set、common_mongod_options/per_mongod_options、common_replica_set_options/per_replica_set_options、persist_connection_stringsMultiShardedClusterFixture参数num_sharded_clusters默认 2要求 ≥ 2、common_mongod_options/per_mongod_options/per_sharded_cluster_options、persist_connection_strings其余共享参数通过**common_sharded_cluster_options透传。这类 Fixture 在多集群迁移、跨集群复制等测试场景中使用。从源码看两者均通过persist_connection_strings将各参与集群的连接串写入config库下的multiReplicaSetFixture/multiShardedClusterFixture集合中供 JS 测试读取。2.5 MongoTFixture搜索拓扑MongoTFixture是提供一个与 mongod 伴生的 mongot的 Fixturemongot.pyL20。其模块文档解释得很清楚mongot 是基于 Lucene 封装的 MongoDB 专用进程为 MongoDB 提供全文搜索能力本地测试使用特殊的 local-dev 二进制使 mongot 与 mongod 在 localhost 上直接通信并通过$changeStream从伴生 mongod 复制数据。在 Suite 的 YAML 中通过在ReplicaSetFixture/ShardedClusterFixture上开启launch_mongot: true并配置keyFile来启用。其工作方式为MongoDFixture构造时为 mongot 分配监听端口mongot_port若useGrpcForSearch开启还会分配mongot_grpc_port并把mongotHost写入 mongod 的set_parametersstandalone.py L100-L110setup_mongot_params()进一步配置 mongot 的mongodHostAndPort指向伴生 mongod与可选的mongosHostAndPort指向分片集群的最后一个 mongos并要求必须提供keyFileL307-L331每个 mongot 拥有独立的 config journal 数据目录data/config_journal_port并在 teardown 时被删除以避免残留索引条目mongot.py L32-L35、L121-L130。2.6 BulkWriteFixturebulkWrite 多集群BulkWriteFixture是为 JSTest 提供一组集群的多集群 Fixturebulk_write.pyL8。它接收cluster_options含class与settings底层集群只允许ReplicaSetFixture或ShardedClusterFixture否则抛出ValueErrorL46-L54。它会自动在settings中注入dbpath_prefixjob 目录/bulkWriteCluster与preserve_dbpath并分别设置replicaset_logging_prefix: bw/cluster_logging_prefix: bw以便日志区分。2.7 YesFixture日志压力工具YesFixture是一个轻量且稍显特殊的 Fixtureyesfixture.pyL8它不启动任何 MongoDB 进程而是通过generic_program启动若干个yes命令yes yyyyyy...持续输出海量日志用于测试日志系统在高负载下的表现。参数为num_instances进程数默认 1与message_length每条消息长度默认 100。2.8 ExternalFixture 与 ExternalShardedClusterFixture外部集群ExternalFixtureexternal.pyL6不启动任何进程而是让 JSTest 连接一个由 resmoke 之外管理的集群。它必须通过 resmoke 的--shellConnString或--shellPort参数提供连接串否则直接抛出ValueErrorL17-L21。其get_driver_connection_url()直接返回用户提供的 URI由于不支持重配外部集群get_internal_connection_string()被实现为NotImplementedErrorL25-L34。ExternalShardedClusterFixtureshardedcluster.py L950组合ExternalFixture与ShardedClusterFixture的能力针对外部如 Docker Compose 启动的分片集群。它通过make_dummy_fixture读取原 Suite 的拓扑信息num_mongos、num_shards据此构造mongodb://mongos0:27017,...连接串setup()阶段会轮询listShards命令最多 50 次、每次间隔 5 秒等待外部集群的分片数量达到预期L965-L982。三、接口层设计四个核心基类与配套工具README 的 Interfaces 一节列出了四个核心抽象它们全部定义在 interface.py 中。3.1 Fixture所有 Fixture 的基类FixtureL90定义了每个 Fixture 必须或可以实现的生命周期与能力方法方法作用setup()创建并启动 Fixture 的进程默认空实现await_ready()阻塞直到 Fixture 可被测试使用默认空实现teardown(finishedFalse, modeNone)销毁 FixturefinishedTrue时关闭日志 handlermode为TeardownMode枚举TERMINATE/KILL/ABORTis_running()返回 Fixture 是否仍在运行pids()返回 Fixture 持有的进程 PID 列表get_node_info()返回NodeInfo列表节点名、端口、PID、版本用于生成节点信息表get_internal_connection_string()返回服务器内部格式的连接串mongo::ConnectionString格式非驱动格式get_shell_connection_string(use_grpcFalse)/get_shell_connection_url()供 mongo shell 执行 jstest 时使用的连接串 / URLget_driver_connection_url()返回驱动PyMongo 等使用的mongodb://连接串mongo_client(...)返回连接该 Fixture 的pymongo.MongoClient支持read_preference与超时当TLS_MODE requireTLS时自动开启 TLS 并加载 CA/证书L271-L299get_environment_variables()返回提供给测试用例的环境变量各 Fixture 可覆盖如MONGODB_FIXTURE_TYPEget_independent_clusters()/get_testable_clusters()返回参与该 Fixture 的独立集群 / 可测试集群列表默认[self]基类还内置了二进制版本探测_get_binary_version运行executable --version并解析db version vX.Y.Z/mongos version vX.Y.Z格式L186-L209供get_node_info与多版本场景使用。值得注意的两个 appName 常量L17-L27RESMOKE_ADMIN_APPNAME Resmoke-Adminresmoke 管理连接Fixture setup/teardown使用豁免 ingress 限流RESMOKE_HOOK_APPNAME Resmoke-Hookresmoke hooks 打开的连接使用同样豁免限流但与管理流量区分以便诊断。对应的三个客户端构造辅助函数L584-L626build_client(...)按auth_optionsusername/password/authSource/authMechanism构造已认证客户端build_admin_client(...)打上Resmoke-AdminappName仅用于 Fixture 的 setup/teardownbuild_hook_client(...)打上Resmoke-HookappName用于 hooks 的连接。此外还有FixtureTeardownHandlerL471用于在集群内逐个拆除节点时收集错误而不是立即抛出所有失败消息会以 - 拼接最终可通过was_successful()与get_error_message()查询。3.2 MultiClusterFixture多参与集群基类MultiClusterFixtureL369是可能由多个独立参与集群组成的 Fixture 基类。README 的原话是参与集群可以无需协调地独立运行但会在参与某过程如迁移的某个时间段内被绑定在一起且每个参与集群本身也是一个 Fixture。它强制子类实现get_independent_clusters()并将get_testable_clusters()默认委托给它。BulkWriteFixture、MultiReplicaSetFixture、MultiShardedClusterFixture均继承自此。3.3 ReplFixture复制拓扑基类ReplFixtureL391是所有支持复制的 Fixture 的基类强制实现get_primary()返回副本集主节点get_secondaries()返回副本集从节点列表并提供了 3.2 节已提到的retry_until_wtimeout重试辅助方法。常量AWAIT_REPL_TIMEOUT_MINS 5与AWAIT_REPL_TIMEOUT_FOREVER_MINS 24 * 60定义了复制同步等待的默认与无限超时。3.4 NoOpFixture不启动任何进程NoOpFixtureL445是一个不启动任何服务器的 Fixture 实现。它用于 MongoDB 部署由 JavaScript 测试自身启动的场景——即测试脚本中直接使用MongoRunner、ReplSetTest或ShardingTest来创建拓扑时resmoke 侧只需一个空壳Fixture 作为占位。其mongo_client()、get_internal_connection_string()、get_driver_connection_url()均返回None或抛NotImplementedErrorL445-L468。3.5 其它配套机制API 版本协商APIVersionL53-L71以语义化版本0.1.0描述interface.py、registry.py、fixturelib.py呈现给 Fixture 的 API 契约通过主版本相等、次版本 ≤ 实际版本的规则做向前兼容判断保障多版本back-branchFixture 的兼容性。FixtureLib 门面FixtureLibfixturelib.py向所有 Fixture 暴露 resmokelib 依赖日志、进程、端口分配、配置读取方法包括make_fixture、new_fixture_node_logger、mongod_program、mongos_program、mongot_program、generic_program、get_next_port、default_if_none、get_config等。端口自动分配即来自get_next_port(job_num)。Docker Compose 接口_DockerComposeInterfaceL322-L366允许实现它的 FixtureMongoDFixture、MongoTFixture、ReplicaSetFixture、ShardedClusterFixture、_MongoSFixture在--dockerComposeBuildImages模式下通过all_processes()程序化生成docker-compose.yml此时节点以NOOP_MONGO_D_S_PROCESSES方式运行以提取启动参数。四、从 Suite 配置到进程启动构造链路与真实示例4.1 真实 Suite 中的 Fixture 配置示例单机 mongodcore.ymlfixture: class: MongoDFixture mongod_options: set_parameters: enableTestCommands: 1副本集aggregation_secondary_reads.ymlfixture: class: ReplicaSetFixture mongod_options: set_parameters: enableTestCommands: 1 # Allow many initial sync attempts. Initial sync may fail if the sync source does not have # an oplog yet because it has not conducted its own initial sync yet. numInitialSyncAttempts: 10000000 linear_chain: true分片集群aggregation_sharded_collections_passthrough.ymlfixture: class: ShardedClusterFixture num_mongos: 3 num_shards: 2 mongos_options: set_parameters: enableTestCommands: 1 mongod_options: set_parameters: enableTestCommands: 14.2 Builder 机制与多版本Multiversion拓扑复杂拓扑由_builder.py中的两个 Builder 组装均在_BUILDERS注册表中登记ReplSetBuilderREGISTERED_NAME ReplicaSetFixtureL187从mixed_bin_versions/old_bin_version中提取多版本选项并校验版本数与节点数的一致性为每个节点创建新/旧两个 mongod Fixture旧版本不启用 feature flags并用FixtureContainerL118-L160包裹使新老 mongod 共用同一端口以便无感升级/降级根据多版本模式计算 FCVLAST_LTS_FCV/LAST_CONTINUOUS_FCV/LAST_PATCH_FCV。ShardedClusterBuilderREGISTERED_NAME ShardedClusterFixtureL446先构建 config server多版本模式下 config server 全部用新版二进制因为官方推荐先升级 config server再逐分片切分mixed_bin_versions子列表构建各分片副本集最后构建 mongosmongos 总是额外准备旧版实例因为它是升级路径上最后被更新的组件。FixtureContainer的关键能力是change_version_if_needed(node)当测试脚本要求升级/降级节点时容器会在新旧 Fixture 之间切换从而实现真正的多版本滚动测试。4.3 生命周期时序以ReplicaSetFixture为例一次测试的典型时序为setup()若启用自动引导先启动节点 0 并await_ready()等待可写主节点出现后通过hello获取自动生成的setName再同步给其余节点replicaset.py L230-L249启动所有 mongod 后执行replSetInitiate并等待主节点选举await_ready()以Resmoke-Admin客户端对主节点执行ping确保集群可用测试执行器将get_shell_connection_string()/get_driver_connection_url()注入 JSTest 环境各 Fixture 还会通过get_environment_variables()提供MONGODB_FIXTURE_TYPE、MONGODB_UDS_PATH等环境变量teardown(mode...)按 TeardownMode 依次停止节点finishedTrue时关闭日志 handler 并清理扩展配置。五、如何选择 Fixture决策速查测试诉求推荐fixture.class测试单个 mongod 行为MongoDFixture测试复制、选举、读偏好等ReplicaSetFixture测试分片、均衡、跨分片事务ShardedClusterFixture测试搜索mongot集成ReplicaSetFixture/ShardedClusterFixturelaunch_mongot: true测试 bulkWrite 多集群行为BulkWriteFixture多副本集 / 多分片集群协同如迁移MultiReplicaSetFixture/MultiShardedClusterFixture部署由 JS 测试自行创建MongoRunner/ReplSetTest/ShardingTestNoOpFixture连接外部托管的集群ExternalFixture/ExternalShardedClusterFixture压测日志系统YesFixture从源码结构看这套设计的关键收益是拓扑与测试逻辑解耦JSTest 只关心连接串与环境变量拓扑细节完全由 Fixture 封装同时通过 Builder 与FixtureContainer优雅地支持了多版本测试这一 MongoDB 特有的复杂场景。若需深入阅读建议从 interface.py基类与接口→ _builder.py组装逻辑→ replicaset.py / shardedcluster.py两大核心拓扑的顺序入手再结合 resmokeconfig/suites 目录下的真实 Suite 配置对照理解。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考