ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SpacetimeDB Rust SDK Procedure 测试客户端实战:从 module_bindings 生成到 procedure ABI 全链路验证

SpacetimeDB Rust SDK Procedure 测试客户端实战:从 module_bindings 生成到 procedure ABI 全链路验证 SpacetimeDB Rust SDK Procedure 测试客户端实战从 module_bindings 生成到 procedure ABI 全链路验证【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB本文以 SpacetimeDB 仓库中的procedure-client测试客户端sdks/rust/tests/procedure-client/README.md为主线系统讲解 Rust 客户端如何通过生成的module_bindings调用模块端#[procedure]定义的过程Procedure并围绕返回值、panic 传播、事务提交/回滚、HTTP 请求与定时调度等场景完成端到端验证。读完本文你将掌握 procedure 在 Rust 模块 ABI 与 Rust SDK 中的完整调用链路并能在本地独立复现、扩展这套测试方法。背景为什么 procedure 需要一套独立的测试客户端在 SpacetimeDB 的模块 ABI 中reducer 是经典的事件驱动入口由客户端调用、触发事务而procedure过程则是一类新的模块导出项它有参数和返回值客户端可以在调用后通过回调拿到模块端返回的结果。正如 modules/sdk-test-procedure/README.md 所述procedure 的模块侧支持在编写时尚未在所有模块语言中普及TypeScript、C# 尚无法实现该模块因此仓库选择先用Rust 模块 Rust 客户端构建一套独立的 procedure 专项测试而不是并入既有的 sdk-test 大测试中。整套测试由三部分组成被测模块modules/sdk-test-procedure一个crate-type [cdylib]的 wasm 模块集中定义了各种行为各异的 procedure 与配套表结构测试客户端sdks/rust/tests/procedure-client一个同时支持 nativeCLI与 wasmbrowser两种构建形态的 Rust SDK 测试程序负责连接数据库、订阅表、调用 procedure 并断言结果测试套件驱动sdks/rust/tests/test.rs通过宏为 Rust/TypeScript/C/C# 四种语言客户端生成同名测试用cargo test -p spacetimedb-sdk procedure一键驱动。测试的核心目标正如 README 原文所述是充分锻炼 Rust 模块 ABI 与 Rust SDK 中与 procedure 相关的各个方面。重新生成 module_bindingsprocedure 调用的基石Rust 客户端要调用模块端的过程必须先由 CLI 从模块源码生成客户端绑定代码module_bindings。原文档给出了在sdks/rust/tests/procedure-client目录下执行的标准命令mkdir -p src/module_bindings spacetime generate --lang rust \ --out-dir src/module_bindings \ --module-path ../../../../modules/sdk-test-procedure各参数含义如下参数作用--lang rust指定生成 Rust 语言绑定--out-dir src/module_bindings绑定输出目录先mkdir -p确保其存在--module-path ../../../../modules/sdk-test-procedure指向被测模块源码目录相对仓库根即为 modules/sdk-test-procedureCLI 会读取其中的模块定义生成对应绑定绑定文件是自动生成的module_bindings/mod.rs 开头的注释明确警告EDITS TO THIS FILE WILL NOT BE SAVED任何对表结构、procedure 签名的修改都应发生在模块源码中再重新运行上述命令。生成结果长什么样mod.rs会把每个过程、reducer、表分别导出为独立模块如return_primitive_procedure、schedule_proc_reducer、my_table_table并聚合出RemoteProcedures过程访问句柄位于DbConnection与各类DbContext的.procedures字段RemoteReducers/RemoteTablesreducer 与表的对应句柄各类事件上下文ProcedureEventContext、SubscriptionEventContext、EventContext、ReducerEventContext、ErrorContextRemoteModule类型作为 SDK 的模块类型参数如DbConnectionBuilderRemoteModule。以 return_primitive_procedure.rs 为例一个#[procedure] fn return_primitive(lhs: u32, rhs: u32) - u32会生成ReturnPrimitiveArgs参数结构体派生Serialize/Deserialize供 BSATN 编码扩展 traitreturn_primitive提供return_primitive(...)忽略结果的快捷调用与return_primitive_then(..., callback)带回调调用两个方法底层实现通过self.imp.invoke_procedure_with_callback::_, u32(return_primitive, ReturnPrimitiveArgs {...}, __callback)走 WebSocket 向服务器发起过程调用回调签名是FnOnce(ProcedureEventContext, ResultT, InternalError)。从中可以看到 procedure 客户端调用的完整数据流参数结构体 BSATN 编码 → 服务器执行 → 返回值 BSATN 解码 → 回调收到ResultT, InternalError。测试客户端架构native 与 wasm 双形态procedure-client的 Cargo.toml 定义了双 featurenative默认编译出 CLI 可执行文件procedure-client依赖env_logger、tokiobrowser编译为wasm32-unknown-unknown目标启用spacetimedb-sdk/browser后端与wasm-bindgen相关依赖。入口也因此分为两处native 入口src/main.rs从命令行参数取测试名从环境变量SPACETIME_SDK_TEST_DB_NAME读取数据库名然后启动 tokio runtime 调用test_handlers::dispatch。它注册了一个自定义 panic hookexit_on_panic任何线程 panic 都会先打印原始 panic 信息再以退出码 1 结束进程从而把测试失败可靠地反映为进程退出码。wasm 入口src/lib.rs通过#[wasm_bindgen]暴露run(test_name, db_name, server_url)并在调用前用test_counter::set_server_url(server_url)显式传入服务器地址——因为 wasm 客户端无法像 native 一样依赖环境变量测试设置必须显式传递。两个入口最终都汇聚到同一个test_handlers::dispatch(test, db_name)保证 native 与 wasm 执行完全相同的测试逻辑这也是 SDK 跨平台一致性测试的典型做法。测试分发与连接基础设施src/test_handlers.rs 的dispatch维护了一张测试名 → 测试函数的映射表CLI 测试名对应测试函数验证要点procedure-return-valuesexec_procedure_return_values原语/结构体/枚举返回值procedure-observe-panicexec_procedure_panic模块端 panic 是否作为Err传播procedure-http-okexec_procedure_http_ok过程内发 HTTP 请求并解析响应procedure-http-errexec_procedure_http_err过程内 HTTP 失败的错误透传insert-with-tx-commitexec_insert_with_tx_commit过程内事务提交后数据可见insert-with-tx-rollbackexec_insert_with_tx_rollback过程内事务回滚后无数据残留schedule-procedureexec_schedule_procedure调度过程按计划时间执行sorted-uuids-insertexec_sorted_uuids_insert过程内生成 1000 个有序 UUIDv7 并插入连接逻辑由两个辅助函数统一封装connect_with_then用DbConnection::builder().with_database_name(name).with_uri(server_url())构建连接注册on_connect与on_connect_error回调并通过build_and_run启动连接驱动循环build_and_run针对两种平台做了区分native 用builder.build()conn.run_threaded()启动后台线程消费 WebSocket 消息wasm 用builder.build().awaitconn.run_background_task()避免阻塞事件循环。connect_then是前者去掉后缀参数的精简版。测试还依赖TestCounter来自 tests/test-counter crate来注册每个异步断言并wait_for_all()汇总等待。逐场景剖析八大测试的验证逻辑返回值测试procedure-return-values被测模块 modules/sdk-test-procedure/src/lib.rs 定义了四类返回值过程return_primitive(lhs, rhs) - u32简单求和验证基本类型的 BSATN 往返return_struct(a, b) - ReturnStruct返回自定义SpacetimeType结构体return_enum_a/return_enum_b分别返回ReturnEnum::A(u32)与ReturnEnum::B(String)两个不同变体。客户端在on_connect回调里并发发起四次调用并在各自的_then回调中对结果做模式匹配断言sum 3、strukt.a 1234 strukt.b foo、matches!(enum_a, ReturnEnum::A(1234))、ReturnEnum::B(string)且内容为foo。值得注意的是枚举携带的 payload 也必须在跨进程传输后保持精确一致这验证了 SATS 类型系统中枚举变体参数的编解码正确性。观察 panicprocedure-observe-panic模块中的will_panic过程体直接panic!(This procedure is expected to panic)。客户端调用will_panic_then并断言回调收到的res是Err——如果模块端 panic 没有正确通过 ABI 传回客户端测试会立即失败提示 Expected failure but got Ok... huh?。这一用例验证了 procedure 与 reducer 在错误传播上的一致性模块端 panic 会转换为客户端可观察的InternalError而不会使客户端连接崩溃。过程内 HTTP 请求procedure-http-ok / procedure-http-errread_my_schema(server_url)过程在模块端发起GET {server_url}/v1/database/{module_identity}/schema?version9请求返回 JSON 字符串。客户端拿到结果后用spacetimedb_lib::de::serde::deserialize_from反序列化为RawModuleDefV9再遍历misc_exports确认存在名为read_my_schema的RawMiscModuleExportV9::Procedure导出项——即用过程自身去读取并校验服务器的模块元数据形成闭环自证。invalid_request过程则向保留无效域名http://foo.invalid/发起请求模块端把Err(e).to_string()作为返回值返回。客户端断言返回字符串同时包含error与http://foo.invalid/验证过程内 HTTP 失败信息能够可靠地透传给调用方。事务提交与回滚insert-with-tx-commit / insert-with-tx-rollback这是对ProcedureContext事务控制能力的核心考验。模块端insert_with_tx_commit通过ctx.with_tx(insert_my_table)提交一条MyTable { field: ReturnStruct { a: 42, b: magic } }随后assert_row_count(ctx, 1)自检insert_with_tx_rollback用ctx.try_with_tx(|ctx| { insert_my_table(ctx); Err(24) })在事务内插入后再返回错误强制回滚随后assert_row_count(ctx, 0)自检。客户端侧通过on_insert回调与订阅后的iter()双重验证commit 场景必须在客户端本地缓存看到该行rollback 场景则注册on_insert(|_, _| unreachable!(...))确保永远不会触发插入回调并在过程回调后断言iter().next() None。定时调度过程schedule-procedure模块端定义了经典 reducer调度表模式reducerschedule_proc向scheduled_proc_table插入一行scheduled_at: duration!(1000ms)同时记录reducer_tsreducer 被调用时刻并携带x: 42, y: 24参数表声明了#[table(accessor scheduled_proc_table, scheduled(scheduled_proc))]表示到期后由调度器调用过程scheduled_proc(data: ScheduledProcTable)调度过程把reducer_ts、procedure_ts过程实际执行时刻连同参数写入proc_inserts_into表。客户端订阅该表并在on_insert回调里断言elapsed procedure_ts - reducer_ts落在 1 秒到 2 秒之间同时校验参数x 42, y 24完整传递。这是对 SpacetimeDB 调度机制ScheduleAt、scheduled()属性、时间戳精度的端到端验证。有序 UUID 生成sorted-uuids-insertsorted_uuids_insert过程在单事务内调用ctx.new_uuid_v7()循环生成 1000 个 UUIDv7 写入pk_uuid表并在模块端自检排序。客户端遍历本地缓存逐行断言严格递增last row.u验证UUIDv7 的时间有序性在过程内批量生成场景下依然成立。运行方式与测试套件集成单跑 procedure 专项测试的标准命令来自 modules/sdk-test-procedure/README.mdcargo test -p spacetimedb-sdk procedure该命令会命中 sdks/rust/tests/test.rs 中procedure_tests!宏展开出的全部过程测试。宏为每种语言客户端构建独立的测试模块procedure_tests!(rust_procedures, ); procedure_tests!(typescript_procedures, -ts); procedure_tests!(cpp_procedures, -cpp); procedure_tests!(csharp_procedures, -cs);每个测试通过spacetimedb_testing::sdk::Test的platform_test_builder完成统一装配指定客户端二进制与子命令、被测模块名sdk-test-procedure及各语言后缀、语言类型并用.with_generate_private_items(true)生成包含私有项在内的完整绑定因为不同语言模块对 scheduled/lifecycle reducer 的默认可见性尚未统一。宏内部还定义了with_tx_commit、with_tx_rollback、http_ok、http_err、schedule_procedure、return_values、observe_panic七个独立#[test]。每个测试都会启动一套独立数据库 独立客户端进程的隔离环境——这也是dispatch注释强调每次订阅测试都运行在全新数据库上、初始表应为空的前提assert_all_tables_empty会在on_subscription_applied回调里做首次空表校验。需要特别说明的运行前提需先安装并启动 SpacetimeDB 服务器standalone测试框架会负责拉起测试数据库实例执行前需按上文命令先生成module_bindings仓库已提交当前生成结果native 形态要求测试进程可读取SPACETIME_SDK_TEST_DB_NAME环境变量由测试框架注入wasm 形态则通过run(test_name, db_name, server_url)显式传参。从源码延伸procedure 与 reducer 的差异及并发补充procedure 与 reducer 在客户端模型上既有共性又有差异从 module_bindings/mod.rs 可以清楚看到两者都通过扩展 trait 挂在连接对象上ctx.procedures与ctx.reducers都遵循xxx_then(args, callback)的异步回调风格差异在于过程回调收到的是ResultT, InternalErrorT 为过程返回值类型而 reducer 回调收到的是ResultResult(), String, InternalError外层为内部错误内层为模块返回的业务错误信息如schedule_proc_then回调中对Ok(Ok(()))/Ok(Err(msg))/Err(...)的三重匹配所示过程回调上下文是ProcedureEventContext无事件字段而 reducer 回调上下文是带ReducerEvent的ReducerEventContext。仓库还在 sdks/rust/tests/procedure-concurrency-client 配套了 procedure 并发专项测试对应rust_procedure_concurrency模块覆盖过程与 reducer 交错执行、同客户端不交错、调度槽独占等并发语义——可见 procedure 测试体系是一整套持续演进的工程质量保障设施。小结通过procedure-client这套测试客户端我们完整走通了 SpacetimeDB Rust 生态中 procedure 的定义模块侧#[procedure]→ 绑定生成spacetime generate→ 客户端调用*_then回调→ 结果断言的全链路并覆盖了返回值类型系统、panic 传播、事务控制、过程内 HTTP、定时调度与 UUID 生成等关键能力。对希望在自己的 Rust 模块中安全使用 procedure 特性的开发者而言这套测试既是可直接运行的行为规范也是理解 SDK 底层实现BSATN 编解码、WebSocket 消息驱动、事件上下文模型的最佳样例代码。【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表