ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NautilusTrader 基础设施集成测试指南:基于 Docker 的 PostgreSQL 与 Redis 测试环境搭建与执行

NautilusTrader 基础设施集成测试指南:基于 Docker 的 PostgreSQL 与 Redis 测试环境搭建与执行 NautilusTrader 基础设施集成测试指南基于 Docker 的 PostgreSQL 与 Redis 测试环境搭建与执行【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_traderNautilusTrader 的nautilus-infrastructurecrate 承载了交易引擎的持久化与消息基础设施基于 Redis 的缓存与消息总线MessageBus、基于 PostgreSQL 的 SQL 缓存。本文以仓库内的 crates/infrastructure/TESTS.md 为核心完整讲解如何通过 Docker Compose 一键拉起 PostgreSQL、Redis、PgAdmin 三套依赖服务并分别在 Python 与 Rust 两层运行基础设施集成测试同时结合.docker/docker-compose.yml、Makefile 与测试源码深入说明这些测试验证了哪些持久化行为、为什么只在 Linux 上运行、以及如何控制并发访问同一数据库的测试。读完本文你将能够独立复现 NautilusTrader 的全部基础设施集成测试流程理解 Redis/PostgreSQL 双后端缓存的底层键结构与写入缓冲机制并掌握cargo nextest过滤器、feature 组合与serial_test串行化的实战用法。一、基础设施集成测试是什么crates/infrastructure是 NautilusTrader 的基础设施组件crateCargo.toml 中将其描述为Infrastructure components for the Nautilus trading engine。它提供两类关键能力Redis 后端RedisCacheDatabase缓存持久化与 Redis 消息总线MessageBus由redisfeature 控制且该 feature 是 crate 的默认 featuredefault [redis]因为nautilus_trader默认依赖它PostgreSQL 后端PostgresCacheDatabase与 SQL 查询层由postgresfeaturedep:sqlx控制。这些组件一旦涉及真实的外部服务Redis、PostgreSQL普通单元测试便无法覆盖。因此仓库将这类测试独立出来放在 crates/infrastructure/tests/integration/ 目录下由 main.rs 聚合四个测试模块test_cache_redisRedis 缓存数据库的增删、索引清理、缓冲刷新、重启恢复等test_redis_queriesRedis 查询层的 SQL 风格查询test_cache_postgresPostgreSQL 缓存数据库的读写与查询test_cache_database_postgresPostgreSQL 数据库适配器层面的集成行为。TESTS.md 明确说明This directory contains infrastructure integration tests that require external services.——这些测试必须依赖外部服务这正是本文要解决的环境搭建问题。二、依赖服务要求与配置所有必需服务统一定义在仓库根目录的 .docker/docker-compose.yml 中共三套服务容器名监听地址用途PostgreSQLnautilus-databaselocalhost:5432主测试数据库Redisnautilus-redislocalhost:6379缓存与消息总线PgAdmin可选nautilus-pgadminhttp://localhost:5051Web 管理界面2.1 PostgreSQL 连接参数TESTS.md 给出的固定连接信息为用户名nautilus、密码pass、数据库名nautilus。在 docker-compose.yml 中这些值实际上支持通过环境变量覆盖并带有默认值environment: POSTGRES_USER: ${POSTGRES_USER:-nautilus} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-pass} POSTGRES_DB: ${POSTGRES_DB:-nautilus} PGDATA: /data/postgres volumes: - nautilus-database:/data/postgres ports: - 127.0.0.1:5432:5432两点值得注意端口绑定为127.0.0.1:5432:5432即只监听本机回环地址不会对外暴露数据库端口适合开发与 CI 场景数据存放在 Docker 命名卷nautilus-database中且容器设置了restart: unless-stopped与security_opt: no-new-privileges:true。2.2 Redis 连接参数Redis 服务使用官方镜像的默认配置、无认证TESTS.md 原文Default configuration, no authentication监听localhost:6379。从测试源码看连接参数由RedisCacheConfig控制见 crates/infrastructure/src/redis/cache.rs测试中实际使用的是RedisCacheConfig { host: Some(localhost.to_string()), port: Some(6379), connection_timeout: 1, number_of_retries: 0, ..Default::default() }测试中刻意把connection_timeout设为 1、重试次数设为 0配合RedisCacheDatabase::new(...)的 2 秒超时——如果本地没有运行 Redis测试会立刻以A running Redis service is required for this test的错误快速失败而不是长时间挂起。2.3 PgAdmin可选PgAdmin 是纯辅助的 Web 管理工具默认登录信息为adminmail.com/admin可通过环境变量PGADMIN_DEFAULT_EMAIL、PGADMIN_DEFAULT_PASSWORD覆盖端口通过PGADMIN_PORT覆盖默认 5051。它不参与任何测试逻辑仅便于人工检查数据。三、通过 Make 目标管理测试服务TESTS.md 提供了四个 Make 目标它们全部定义在 Makefile 中。下表汇总了每个目标的行为命令作用Makefile 实际执行内容make init-services启动容器并初始化数据库 schema首次使用先执行start-services等待 10 秒让 PostgreSQL 就绪再执行init-dbmake stop-services停止开发服务保留数据卷docker compose -f .docker/docker-compose.yml downmake start-services启动开发服务不重新初始化数据库拉取镜像带重试后docker compose up -dmake purge-services彻底移除包括数据卷docker compose -f .docker/docker-compose.yml down -v3.1 init-services首次初始化的完整流程init-services是开箱即用的入口其内部拆解如下init-services: $(MAKE) start-services printf $(PURPLE)Waiting for PostgreSQL to be ready...$(RESET)\n sleep 10 $(MAKE) init-db其中start-services会先通过scripts/ci/docker-pull-retry.sh对postgres、pgadmin4、redis三个镜像执行带重试的拉取再以docker compose -f .docker/docker-compose.yml up -d后台启动全部容器。而init-db负责真正的 schema 初始化将 schema/sql/ 目录下的四个 SQL 文件按顺序灌入容器内的 PostgreSQLinit-db: cat schema/sql/types.sql schema/sql/tables.sql schema/sql/functions.sql schema/sql/partitions.sql \ | docker exec -i nautilus-database psql -U nautilus -d nautilustypes.sql自定义类型枚举、复合类型tables.sql各业务表结构functions.sql存储函数partitions.sql分区表定义。注意执行顺序不可调换——类型必须先于表、函数先于分区创建这是 PostgreSQL 的依赖顺序决定的。3.2 典型工作流TESTS.md 给出的标准使用流程是一个完整的生命周期首次使用make init-services启动 初始化 schema测试完毕make stop-services停止容器数据保留在卷中再次开发make start-services无需重新初始化数据库直接继续需要完全干净的环境make purge-services清空数据卷再make init-services重建。这套循环的价值在于stop-services/start-services之间数据卷不会丢失你可以中断测试、重启机器后继续不必每次重建 schema而purge-services则用于排除脏数据干扰确保从零验证。四、运行 Python 基础设施集成测试前提容器服务已运行且 NautilusTrader 已通过uv或make安装TESTS.md 原文Once services are running (and NautilusTrader installed byuvormake)。# 运行全部基础设施测试 uv run --no-sync pytest tests/integration_tests/infrastructure/ # 运行单个测试文件 uv run --no-sync pytest tests/integration_tests/infrastructure/test_cache_database_redis.py uv run --no-sync pytest tests/integration_tests/infrastructure/test_cache_database_postgres.py关键点解读命令路径tests/integration_tests/infrastructure/是相对python/目录的测试树对应 python/tests/--no-sync告诉uv不要在执行前同步虚拟环境依赖从而避免与仓库构建流程产生副作用适合依赖已就绪的增量执行场景单文件执行模式便于聚焦调试某一个后端Redis 或 PostgreSQL的缓存适配器行为。五、运行 Rust 基础设施集成测试Rust 侧集成测试位于crates/infrastructure/tests/与 Python 侧共享同一套外部服务。TESTS.md 提供了三种执行方式。5.1 最简方式Make 封装make cargo-test-crate-nautilus-infrastructure该目标遵循 Makefile 中cargo-test-crate-%的通用模式Makefile 中声明为 Cargo 构建目标之一自动带上仓库约定的 feature 集合与 nextest 配置是 CI 与本地最省心的入口。5.2 直接使用 cargo nextest推荐用于调试# 运行全部基础设施测试输出可见以便调试 cargo nextest run --lib --no-fail-fast --cargo-profile nextest \ -p nautilus-infrastructure --features redis,postgres --no-capture # 仅运行 Redis 集成测试 cargo nextest run --lib --no-fail-fast --cargo-profile nextest \ -p nautilus-infrastructure --features redis,postgres -E test(test_cache_redis) # 仅运行 PostgreSQL 集成测试 cargo nextest run --lib --no-fail-fast --cargo-profile nextest \ -p nautilus-infrastructure --features redis,postgres \ -E test(test_cache_postgres) or test(test_cache_database_postgres)参数逐一说明参数作用--lib只构建并运行库目标跳过不必要的二进制目标--no-fail-fast单个测试失败后继续运行其余测试便于一次看到全部失败--cargo-profile nextest使用仓库约定的nextest编译 profile-p nautilus-infrastructure限定包名只编译本 crate--features redis,postgres同时启用两个后端 feature--no-capture透传测试输出println!等调试时可见中间状态-E ...nextest 的表达式过滤器filterset按测试名筛选5.3 为什么示例同时带redis,postgres两个 featureTESTS.md 特别注明Both Redis and PostgreSQL feature flags are in given examples to avoid rebuild.原因是两个 feature 组共用同一份编译产物如果在一次本地会话中先只启用redis跑一遍、再只启用postgres跑一遍cargo 会因为 feature 集合变化而重新编译整个 crate。同时带上两个 feature能让后续任意筛选的执行都命中已编译的缓存显著缩短迭代时间。5.4 平台与并发约束TESTS.md 末尾给出了三条重要限制均能在源码中得到印证仅限 Linux。Rust 集成测试用#[cfg(target_os linux)]标注在非 Linux 平台macOS/Windows编译期即被剔除。测试文件中的注释也写明Databases only tested and supported on Linux例如 test_cache_redis.rs 顶部的三重 cfg 组合#[cfg(test)] #[cfg(feature redis)] #[cfg(target_os linux)] // Databases only tested and supported on Linux mod serial_tests { ... }同一数据库的测试必须串行。测试使用serial_test风格的手写互斥锁test_cache_redis.rs保证访问同一数据库的用例不并发fn redis_test_mutex() - static tokio::sync::Mutex() { static LOCK: OnceLocktokio::sync::Mutex() OnceLock::new(); LOCK.get_or_init(|| tokio::sync::Mutex::new(())) }每个测试开头let _guard redis_test_mutex().lock().await;持有互斥锁防止多个测试同时向 Redis 写入互相污染。TESTS.md 原文的表述是 They use theserial_testcrate to ensure tests that access the same database dont run concurrently——当前实现为基于tokio::sync::Mutex的等价串行化机制。服务未启动会快速失败。Redis 测试通过RedisCacheDatabase::new(...)连接失败并抛出A running Redis service is required for this testPostgreSQL 测试则用 2 秒超时包裹get_pg_cache_database()见 test_cache_postgres.rs同样给出A running PostgreSQL service is required for this test的明确报错。六、深入源码这些集成测试到底验证了什么结合测试源码可以看清nautilus-infrastructure持久化层的关键设计这也是这些测试存在的意义。6.1 键命名空间trader_keyRedis 缓存的所有键都以trader_key为前缀其格式为{trader_id}:{instance_id}。测试断言了大量形如{trader_key}:orders:{client_order_id}、{trader_key}:positions:{position_id}、{trader_key}:accounts:{account_id}的键结构。这种命名空间设计使同一 Redis 实例可以承载多个 trader/实例的数据而不冲突同时为重启恢复提供了基础——同一 trader 下重建 adapter 就能读到之前写入的数据。6.2 索引一致性与幂等删除test_cache_redis.rs 中的test_delete_order_cleans_up_indexes是重点用例它先把订单 ID 写入六类基于 Set 的索引index:order_ids、index:orders、index:orders_open、index:orders_closed、index:orders_emulated、index:orders_inflight和两类基于 Hash 的索引index:order_position、index:order_client再调用delete_order断言所有索引同步清除。这验证了删除操作不仅是删除主记录还必须级联清理全部反向索引否则重启后缓存重建会产生幽灵数据。另有test_delete_operations_are_idempotent对同一订单/仓位连续删除三次验证删除幂等性test_delete_account_event_is_a_no_op则明确标注账户事件删除当前是documented no-op pending redesign。6.3 写入缓冲buffer_interval_ms 的三种行为缓存写入并非每次都直连 Redis而是通过缓冲批量落盘。CacheConfig的buffer_interval_ms字段控制三种模式见 test_cache_redis.rs 与 crates/infrastructure/src/redis/cache.rs 的Duration::from_millis(config.buffer_interval_ms.unwrap_or(0) ...)Some(50)即使没有新消息到达定时器也会周期性冲刷缓冲对应 issue #3426 的空闲期阻塞修复测试注释明确引用该 issueSome(0)写入立即冲刷不经过定时等待Some(10000)写入滞留缓冲直到显式调用close()时才排空——test_buffer_drains_on_close先断言数据未落库再在close()后断言数据已写入。6.4 重启恢复与自定义数据往返test_index_order_clients_batch_survives_restart验证了最具生产价值的场景先写入订单及订单→Client映射索引然后断开 adapter 重建连接模拟引擎重启再通过Cache::cache_orders()与build_index()恢复断言订单、Client 映射全部还原。test_instrument_close_replacement_survives_restart则验证InstrumentClose的后写覆盖先写语义在重启后依然成立。此外test_add_and_load_custom_data_roundtrip与test_load_custom_data_filters_by_identifier验证自定义数据类型可完整往返存取并能按DataType的标识符过滤查询。6.5 PostgreSQL 侧PostgreSQL 侧测试test_cache_postgres.rs、test_cache_database_postgres.rs同样覆盖订单、仓位、账户事件的完整生命周期其表结构由 schema/sql/ 下的 SQL 文件定义与make init-db灌入的 schema 一一对应。SQL 查询层位于 crates/infrastructure/src/sql/queries.rsRedis 查询层位于 crates/infrastructure/src/redis/queries.rs二者实现了相同的DatabaseQueriestrait 接口测试中统一以nautilus_infrastructure::sql::queries::DatabaseQueries/nautilus_infrastructure::redis::queries::DatabaseQueries引入。七、常见问题与排查要点现象排查方向测试报A running Redis service is required for this test检查docker compose ps中nautilus-redis是否在运行Redis 配置为无认证确认本地 6379 未被其他程序占用测试报A running PostgreSQL service is required for this test: connection timed out首次运行必须先执行make init-services含 10 秒等待 schema 初始化仅start-services不会建表修改 SQL schema 后测试仍用旧表结构执行make purge-services删除数据卷再make init-services全新初始化macOS/Windows 上找不到任何 Rust 集成测试属于预期行为测试通过#[cfg(target_os linux)]仅限 Linux可在 Linux 容器或 CI 中运行多个测试相互干扰、数据污染这些测试已内置互斥串行化若手动向 Redis 写入过测试键可执行make purge-services清场八、总结NautilusTrader 的基础设施集成测试体系可以概括为一句话一套 Docker Compose 服务定义PostgreSQL Redis PgAdmin、四个 Make 目标init/start/stop/purge-services承载服务生命周期Python 与 Rust 两侧共享同一环境。Rust 侧通过 feature 开关redis/postgres、Linux-only 的 cfg 约束与互斥串行化保证了持久化层在真实外部服务下的行为可验证、可重复测试覆盖从 Redis 键命名空间、索引级联清理、写入缓冲三种时序到引擎重启后的数据恢复等关键生产语义。无论你是要为 NautilusTrader 贡献持久化代码还是想在自己的项目中复刻这套外部服务依赖型测试的工程范式都可以直接以 crates/infrastructure/TESTS.md、.docker/docker-compose.yml 与 crates/infrastructure/tests/integration/ 为蓝本开始实践。【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表