ARTICLE DETAIL

资讯详情

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

Rivet Rust SDK 响应模型 RunnersListNamesResponse 深度解析:Runner 名称列举与游标分页

Rivet Rust SDK 响应模型 RunnersListNamesResponse 深度解析:Runner 名称列举与游标分页 Rivet Rust SDK 响应模型 RunnersListNamesResponse 深度解析Runner 名称列举与游标分页【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors导读RunnersListNamesResponse是 Rivet 公开 API 中GET /runners/names接口的响应模型用于按 namespace命名空间轻量列举所有 Runner 的名称。与返回完整 Runner 详情的runners_list不同该接口只返回名称字符串列表并附带一个游标cursor用于分页是批量发现、巡检和全量枚举 Runner 时的首选接口。读完本文你将掌握该模型的字段结构、游标分页的完整语义、Rust SDK 中的调用方式以及从 API 网关到 Pegboard 存储层Universaldb的完整实现链路。模型总览两个必填字段根据 RunnersListNamesResponse.md 中的定义该模型仅包含两个属性且均为必填NameTypeDescriptionNotesnamesVecStringRunner 名称列表paginationmodels::Pagination分页信息游标对应的 Rust 结构体定义在 src/models/runners_list_names_response.rs#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct RunnersListNamesResponse { #[serde(rename names)] pub names: VecString, #[serde(rename pagination)] pub pagination: Boxmodels::Pagination, } impl RunnersListNamesResponse { pub fn new(names: VecString, pagination: models::Pagination) - RunnersListNamesResponse { RunnersListNamesResponse { names, pagination: Box::new(pagination), } } }几点值得注意该文件由 OpenAPI Generator 基于 OpenAPI 规范 2.3.14 自动生成见文件头注释因此字段名与 JSON 响应体中的键一一对应names、pagination。生成代码提供了RunnersListNamesResponse::new(names, pagination)构造器方便在测试或 mock 场景中快速构造响应对象。pagination被包装在Box中避免大结构体在栈上复制。服务端的同源类型定义位于 engine/packages/api-types/src/runners/list_names.rs并通过#[schema(as RunnersListNamesResponse)]声明 OpenAPI 名称与客户端模型对齐#[derive(Serialize, Deserialize, ToSchema)] #[serde(deny_unknown_fields)] #[schema(as RunnersListNamesResponse)] pub struct ListNamesResponse { pub names: VecString, pub pagination: Pagination, }Pagination游标分页的载体pagination字段的类型为 Pagination其定义极为精简只有一个可选字段NameTypeDescriptionNotescursorOptionString下一页游标[optional]对应生成的客户端代码见 src/models/pagination.rspub struct Pagination { #[serde(rename cursor, default, with ::serde_with::rust::double_option, skip_serializing_if Option::is_none)] pub cursor: OptionOptionString, }这里生成的客户端使用double_optionOptionOptionString来区分字段缺失与字段为 null两种情况这是 OpenAPI 生成器对可选字段的保守处理方式服务端定义则更直接见 engine/packages/api-types/src/pagination.rspub struct Pagination { pub cursor: OptionString, }游标语义从服务端实现可确认当一页仍有更多数据时cursor为本页最后一个 Runner 名称的字符串请求方将上一页的cursor作为下一请求的cursor查询参数传入当响应中pagination.cursor为None或names为空时说明已经到达最后一页游标本质上是从某个名称之后继续扫描的位置标记服务端将其转换为底层存储的键范围扫描起点详见下文实现链路。背后的接口GET /runners/namesRunnersListNamesResponse是 RunnersApi 中runners_list_names方法的返回类型。该接口的完整定义如下HTTP 方法/路径GET /runners/names认证方式bearer_authBearer Token请求头Content-Type未定义Accept: application/json往返说明文档明确标注为 Datacenter Round Trips共 2 次数据中心往返GET /runners/namesfanout扇出到所有数据中心[api-peer] namespace::ops::resolve_for_name_global将 namespace 名称解析为全局 namespace ID查询参数NameTypeRequiredNotesnamespaceString是目标命名空间名称limitOptioni32否每页返回的最大名称数cursorOptionString否上一页返回的游标一个典型的响应示例{ names: [alpha-runner, beta-runner, zebra-runner], pagination: { cursor: zebra-runner } }当没有更多数据时响应形如{ names: [alpha-runner], pagination: {} }即cursor字段缺失表示已到最后一页。Rust SDK 调用方式SDK 中runners_list_names的函数签名与实现在 src/apis/runners_api.rspub async fn runners_list_names( configuration: configuration::Configuration, namespace: str, limit: Optioni32, cursor: Optionstr, ) - Resultmodels::RunnersListNamesResponse, ErrorRunnersListNamesError该函数会将namespace、limit、cursor拼装为查询参数使用reqwest以GET方式请求{base_path}/runners/names若配置了bearer_access_token则自动附加 Bearer 认证头成功时非 4xx/5xx将响应体按application/json反序列化为RunnersListNamesResponse否则返回带状态码与错误体的Error::ResponseError。一个完整的分页遍历示例use rivet_api_full::apis::{configuration::Configuration, runners_api}; let mut config Configuration::new(); config.bearer_access_token Some(your_token.to_string()); // 默认 base_path 为 http://localhost可按实际网关地址覆盖 config.base_path https://api.example.com.to_string(); let namespace my-namespace; let mut cursor: OptionString None; loop { let resp runners_api::runners_list_names( config, namespace, Some(50), // 每页最多 50 个名称 cursor.as_deref(), ) .await?; for name in resp.names { println!(runner: {name}); } // 没有游标或本页为空即遍历结束 match resp.pagination.cursor { Some(next) cursor Some(next), None break, } }若你已有原始 JSON 响应也可以直接手动反序列化所有模型均实现了serde::Deserializeuse rivet_api_full::models::RunnersListNamesResponse; let parsed: RunnersListNamesResponse serde_json::from_str(json_str)?; assert!(!parsed.names.is_empty()); if let Some(cursor) parsed.pagination.cursor { // 继续请求下一页 }服务端实现链路从扇出到存储扫描理解RunnersListNamesResponse的生成过程有助于把握其字段语义与性能特征。整条链路共分三层均可在本仓库中直接查看。第一层api-public 网关扇出与聚合入口位于 engine/packages/api-public/src/runners.rs。list_names_inner的核心逻辑先执行ctx.auth().await?做 Bearer 认证将请求通过fanout_to_datacenters扇出到所有数据中心聚合每个数据中心返回的names聚合容器使用IndexSetString天然去重对聚合结果执行.take(limit)limit缺省时取 100即query.limit.unwrap_or(100)对名称执行sort()保证结果按字母序稳定输出以最后一个名称为游标构造ListNamesResponse { names, pagination: Pagination { cursor } }返回。第二层api-peernamespace 解析与 Pegboard 调用每个数据中心的 peer 处理器位于 engine/packages/api-peer/src/runners.rslet namespace ctx .op(namespace::ops::resolve_for_name_global::Input { name: query.namespace.clone(), }) .await? .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; let list_res ctx .op(pegboard::ops::runner::list_names::Input { namespace_id: namespace.namespace_id, after_name: query.cursor.clone(), // 游标转换为从该名称之后的扫描起点 limit: query.limit.unwrap_or(100), }) .await?;这里对应了文档标注的第 2 次数据中心往返resolve_for_name_global把用户可读的 namespace 名称解析为全局唯一的namespace_id随后以namespace_id after_name limit为输入调用 Pegboard 操作。若 namespace 不存在则返回Namespace::NotFound错误对应测试list_runner_names_with_non_existent_namespace所验证的行为。第三层Pegboard 操作Universaldb 键范围扫描真正的数据读取在 engine/packages/pegboard/src/ops/runner/list_names.rs。它在一个 Universaldb 事务内完成let runner_name_subspace keys::subspace().subspace(keys::ns::RunnerNameKey::subspace(input.namespace_id)); let (start, end) runner_name_subspace.range(); let start if let Some(name) input.after_name { universaldb::utils::end_of_key_range(tx.pack(keys::ns::RunnerNameKey::new( input.namespace_id, name.clone(), ))) } else { start }; tx.get_ranges_keyvalues( universaldb::RangeOption { mode: StreamingMode::Exact, limit: Some(input.limit), ..(start, end).into() }, Snapshot, // 非 Serializable避免与新名称写入竞争 ) .map(|res| { let key tx.unpack::keys::ns::RunnerNameKey(res?.key())?; Ok(key.name) }) .try_collect::Vec_()几个值得展开的实现细节键结构RunnerNameKey定义在 engine/packages/pegboard/src/keys/ns.rs编码为元组(NAMESPACE, namespace_id, RUNNER, NAME, name)。由于 Universaldb 的元组编码天然有序同一 namespace 下的 Runner 名称按键字典序排列——这正是响应中名称按字母序返回、以及以名称为游标可行的根本原因。游标推进end_of_key_range见 engine/packages/universaldb/src/utils/mod.rs在给定键后追加一个0字节从而构造一个严格大于该键的扫描起点实现从after_name之后继续分页的效果。隔离级别扫描使用Snapshot隔离而非Serializable源码注释明确说明这是为了避免与新增名称的写入产生冲突——列表操作允许读到稍旧的快照以换取更低的事务争用。分页终止条件当范围内已无更多键时返回的名称列表为空或不足一页此时上层list_res.names.last()为Nonepagination.cursor即为None请求方据此结束遍历。测试验证行为即规格仓库为该接口提供了完整的端到端测试见 engine/packages/engine/tests/runner/api_runners_list_names.rs可作为行为契约参考list_all_runner_names_in_namespace在含 Runner 的 namespace 中列举名称断言结果非空且包含测试 Runner 名称list_runner_names_with_pagination/list_runner_names_pagination_no_duplicates_comprehensive创建 59 个顺序命名的 Runner以limit3逐页拉取断言每页数量、页间无重复名称、最终能遍历全部名称带 20 页的安全上限防止死循环这两个用例因旧版 Pegboard Runner 全量测试超时而标记为#[ignore]但逻辑仍有效list_runner_names_returns_empty_for_empty_namespace空 namespace 返回空列表list_runner_names_empty_response_no_cursor空响应的pagination.cursor必须为Nonelist_runner_names_with_non_existent_namespace不存在的 namespace 返回错误list_runner_names_default_limit_100不传limit时结果不超过默认值 100list_runner_names_alphabetical_sorting乱序创建的名称zebra-runner、alpha-runner、beta-runner列举结果按字母序排列。这些测试与源码实现相互印证构成了RunnersListNamesResponse字段语义的完整证据链names是有序、无重复、受limit约束的字符串列表pagination.cursor是继续往下翻的位置标记只在还有下一页时出现。与其他模型的关联RunnersListResponse同样是GET /runnersrunners_list的响应模型返回完整的Runner对象列表含create_ts等元数据分页游标基于创建时间戳当只需名称做发现/巡检时RunnersListNamesResponse更轻量。ActorsListNamesResponseGET /actors/names的响应模型结构与RunnersListNamesResponse完全一致namespagination对应 Actor 名称的列举场景。两者共享同一套Pagination类型与游标分页机制底层实现也可相互参照pegboard::ops::actor::list_names。小结与注意事项RunnersListNamesResponse只有namesVecString与paginationPagination两个必填字段结构简单但承载着完整的游标分页协议。分页协议要点cursor回传上一页最后一个名称limit缺省为 100cursor为None即遍历结束名称结果按字母序返回且跨页去重。从性能角度该接口按名称进行存储键范围扫描并刻意使用Snapshot隔离级别降低写竞争适合高频的全量枚举场景若需要 Runner 的完整元数据则应改用GET /runners。依赖该接口时请注意namespace 不存在会返回Namespace::NotFound错误需要在客户端做好错误处理SDK 会以Error::ResponseError返回含状态码的错误体。【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表