Rerun Catalog 对象模型:从表、数据集到分段与资源的管理体系
Rerun Catalog 对象模型从表、数据集到分段与资源的管理体系【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读本篇技术指南围绕 Rerun 的Catalog目录服务器对象模型展开系统讲解目录中两类顶级条目Entry——表Table与数据集Dataset——的组织方式、命名与重命名规则、目录式分层结构以及数据集内部分段Segment/层Layer的注册与覆盖语义。无论你是要组织海量机器人多模态数据、将.rrd录制注册进数据集还是想借助资产Asset与蓝图Blueprint复用静态数据与视图配置本文都会给出基于 Catalog SDK 的可运行示例并结合 re_server 的 Rust 源码揭示底层实现原理。背景知识关于录制Recording与日志记录基础可先阅读 Recordings关于 Chunk 数据模型参见 Chunks 与 Entity Components、Timelines。Catalog顶层对象模型我们把某个 Catalog 服务器中存储的内容统称为catalog目录。目录的顶层对象称为entries条目目前存在两类条目tables表和datasets数据集下文将分别详述。所有条目共享一组公共属性id全局唯一标识符Entry ID。它是不可变的条目一经创建便不再改变name用户提供的名称必须在整个目录内唯一。这些属性在 Python SDK 的Entry基类中均有对应实现见 rerun_py/rerun_sdk/rerun/catalog/_entry.pyid、name、kind条目类型、created_at创建时间与updated_at最后更新时间均为只读属性此外还有delete()删除方法。在服务端re_server/src/store/dataset.rs 中的as_entry_details()同样把id、name、kind、created_at、updated_at组装为EntryDetails从 Rust 侧印证了这套公共模型。重命名条目条目的id不可变但name可以修改前提是修改后仍保持唯一。在 Python 中调用条目对象的set_name()即可在目录服务器上完成重命名client rr.catalog.CatalogClient(…) dataset client.get_dataset(old_name) dataset.set_name(new_name)关于命名规则SDK 文档与代码中有明确的约束见 _entry.py 的Entry.set_name与 _catalog_client.py 的create_dataset名称必须是非空 ASCII 字符串最长 180 个字符允许的字符集包括字母数字、下划线_、连字符-、点.、冒号:、空格以及方括号[ ]名称在目录内必须唯一重名会抛出错误create_dataset(name, exist_okTrue)可以容忍已存在的情况并返回现有条目。服务端同样会校验名称唯一性Entry的__eq__支持与str、EntryId比较从而可以实现entry_name in client.entries()、entry_id in client.entries()这类惯用写法。注意旧的Entry.update()方法已标记为弃用请改用set_name()。用点号分隔构造目录式结构当数据量较大时可以用类似文件系统目录的方式组织条目在名称中使用.作为分隔符。例如名为robot_tasks.pick_place的数据集会在 Viewer 的数据源浏览器data source browser中显示为robot_tasks之下的pick_place目录树节点。这种点号命名法充分利用了名称中允许.字符的规则让条目组织天然具备层级语义。注目录树仅是一种显示层面的组织约定底层仍以点号名称的字符串匹配实现它不影响id的唯一性与name的全局唯一性约束。表条目Table entriesschema-on-write 的 Arrow 表表条目建模单张数据表。它使用 Arrow 数据模型因此一张表在逻辑上等价于一个 Arrow Table并拥有对应的 Arrow Schema。由于 schema 在建表时即已确定表的模式被称为schema-on-write先定义模式再写入数据这与数据集见下文的schema-on-read形成鲜明对比。在 Python SDK 中TableEntry类见 _entry.py实现为 DataFusion 的 table providerreader()返回datafusion.DataFrameto_arrow_reader()可转换为pyarrow.RecordBatchReaderarrow_schema()直接返回 Arrow schema。通过CatalogClient.create_table(name, schema, url)可以建表url指定存储目录缺省时由服务器根据其可写存储自动生成register_table(name, url)则可以把一个已存在的 Lance 表注册为表条目。表支持的三类写操作通过 Catalog SDK表支持以下三类变更操作操作语义说明append追加向表中添加新行不改变已有数据overwrite整体覆盖用新数据替换整张表upsert按索引列替换基于索引列匹配用新数据替换已有行类似数据库的 upsert在 _entry.py 中可以找到append()与overwrite()的实现二者都接受_BatchesType类型的输入——可以是RecordBatchReader、单个RecordBatch、RecordBatch列表或datafusion.DataFrame.collect()返回的批次列表的列表同时也可以直接用**named_params以命名参数方式按列写入。三种操作底层分别映射到TableInsertModeInternal.APPEND、OVERWRITE、UPSERT三种插入模式。基于 DataFusion 的数据库级查询得益于 DataFusion表还支持绝大多数数据库操作查询、过滤、连接join等。TableEntry通过__datafusion_table_provider__将自己暴露给 DataFusion 会话因此可以无缝地参与 SQL 式查询管道例如client rr.catalog.CatalogClient(…) table client.get_table(metrics) df table.reader() # datafusion.DataFrame result df.filter(col(value) 0).collect()提示使用 Catalog 客户端需要安装datafusionPython 包CatalogClient初始化时会检查其存在性与 FFI 兼容的主版本号见 _catalog_client.py。数据集条目Dataset entriesschema-on-read 的分段模型数据集条目建模一组按 episode片段组织的 Rerun 数据例如某个机器人任务的多次运行记录。数据集内部的分段称为segments分段由segment ID标识。分段与层的注册语义分段通过注册registering一条录制通常存放在 S3 等对象存储中到数据集来创建使用 Catalog SDK 完成。.rrd文件的 recording ID 被直接用作其 segment ID——这保证了同一份录制的重复注册可以被稳定识别。注册到同一分段的录制按层layer组织层由layer name标识。默认层名为base。关键语义如下向同一数据集、以相同 segment ID即相同 recording ID和相同 layer name注册两个.rrd第二个会**覆盖overwrite**第一个若想叠加式additive注册则为同一 segment ID 的多个.rrd使用不同的 layer name。因此层是不可变的只能通过注册新的.rrd被覆盖。数据集支持的变更操作可以归纳为操作触发方式create segment创建分段以新的 recording ID 注册.rrdappend to segment向分段追加以匹配的 recording ID 新的 layer name 注册.rrdoverwrite segment layer覆盖分段层以匹配的 recording ID 已存在的 layer name 注册.rrd在 SDK 层面DatasetEntry.register()见 _entry.py提供layer_name默认base可传字符串或与recording_uri等长的序列与on_duplicate参数on_duplicate类型为OnDuplicateSegmentLayer枚举取值ERROR默认重复时报错、SKIP跳过重复层、REPLACE用新数据替换已有层建议将大量 URI 批量放入一次register调用而不是循环逐个注册后者明显更慢另有register_prefix(recordings_prefix, …)可以递归注册对象存储某个前缀目录式路径下的所有.rrdunregister(segments_to_drop, layers_to_drop, forceFalse)按外积语义删除空 segments 非空 layers 删除所有分段的指定层非空 segments 空 layers 删除指定分段的全部层二者都非空则删除指定分段上的指定层。服务端实现印证在 re_server/src/store/dataset.rs 的add_source()中新注册的源会以(segment_id, layer_name)为键插入segments: HashMapSegmentId, Segment而 re_server/src/store/segment.rs 的Segment::insert_source()依据IfDuplicateBehavior对应 SDK 的OnDuplicateSegmentLayer返回Inserted / Overwritten / Skipped / LayerAlreadyExists四种结果——这正是上述新建/追加/覆盖/报错语义的 Rust 落地。register_rrd()dataset.rs则演示了服务端如何把.rrd文件按store_id.recording_id()映射为 segment ID 并注册。数据集的分层 schema数据集基于 Rerun 数据模型即一组 Chunk块 的集合chunk 中承载着对应各种索引即 Timelines时间线下的多个 Entity实体与 Component组件。一个给定的 chunk 集合比如一个数据集分段定义了自身的 Arrow schema。我们称之为schema-on-read模式由数据推导而来而非预先定义——这与表模型的 schema-on-write 正好相反。在这种语境下数据集的 schema 其所有分段 schema 的并集分段的 schema 其所有层 schema 的并集。服务端实现中Dataset::schema()dataset.rs用Schema::try_merge合并所有 source 的 schema并带时间戳缓存仅在updated_at变化时重算。segment_table()dataset.rs则把每个分段的layer_names、storage_urls、last_updated_at、num_chunks、size_bytes以及各索引timeline的start/end范围汇总为一张 RecordBatch——这就是 SDK 中segment_table()元数据表的数据来源其rerun_segment_id列可用来与自定义元数据 join。数据集的最小 schema 自洽性数据集维持最小程度的 schema 自洽性注册一个与当前数据集 schema 不兼容的.rrd会报错。这里不兼容的精确含义是新的.rrd对同一个 entity、archetype、component声明了列但Arrow 类型不同。这种冲突在实际中很罕见使用标准 Rerun archetype 时几乎不可能发生因为同一组件类型天然对应同一 Arrow 类型。Rust 侧的校验在add_source()中进行dataset.rs对每个新字段若在现有 schema 中找到同名字段但类型不一致则触发Error::SchemaConflict并调用reject_unsupported_widenings拒绝不支持的 widen类型放宽操作随后整体Schema::try_merge失败同样以 SchemaConflict 报错。为数据集分配蓝图Blueprints数据集可以分配一个blueprint蓝图将一个通常存放在对象存储中的.rbl蓝图文件注册到数据集即可。Catalog SDK 提供了专用 APIDatasetEntry.register_blueprint()。注册后该蓝图会被应用到数据集的所有分段——在 Rerun Viewer 中可视化这些分段时统一生效。register_blueprint(uri, set_defaultTrue, *, segment_tableFalse)见 _entry.py默认同时把该蓝图设为默认蓝图设置segment_tableTrue并保持set_defaultTrue则将其注册为数据集分段表蓝图的默认值。SDK 还提供blueprints()列出已注册蓝图、set_default_blueprint()/default_blueprint()、set_default_segment_table_blueprint()/default_segment_table_blueprint()等管理方法。与资产类似蓝图存放在独立的数据集blueprint dataset中该数据集归宿主数据集所有owned删除宿主数据集会一并删除关联的蓝图数据集及其存储blueprint_dataset()的 docstring 与 Rust 侧DatasetKind::Blueprint的 entry kind 映射都印证了这一点见 dataset.rs。blueprint_dataset()返回None表示该数据集未配置蓝图数据集。资产Assets跨分段共享的静态数据资产asset是注册在数据集上、被其所有分段共享的静态数据例如机器人 URDF、房间网格room mesh或标定数据calibration。把这类数据注册一次作为资产就无需在每个录制中重复拷贝。与蓝图一样资产存放在**属于自己的数据集asset dataset**中该数据集归宿主数据集所有删除宿主数据集会同时删除其资产数据集与资产存储。asset_dataset()_entry.py用于获取它assets()列出当前已注册的全部资产。资产从一个服务器可读取的.rrd文件注册而来使用DatasetEntry.register_asset()client rr.catalog.CatalogClient(…) dataset client.get_dataset(my_dataset) asset_id dataset.register_asset(s3://bucket/robot_mesh.rrd) print(dataset.assets()) dataset.unregister_asset(asset_id)register_asset(uri)返回所注册资产的segment ID旧数据集若尚无资产数据集会先调用内部接口_ensure_asset_dataset()让服务器补建见 _entry.pyunregister_asset(segment_id)同步阻塞直到注销完成注销不存在的资产是无操作no-op。由于资产在所有分段间共享无法把某个资产限定到部分分段——移除它就是在数据集层面整体移除。资产数据集的服务端限制为了让资产数据集保持轻量服务器会拒绝满足以下任一条件的注册这些限制在 Rust 侧DatasetKind::limits()与enforce_limits()中强制实施见 dataset.rs包含时序数据资产只能包含静态数据static_chunks_only检查存在 temporal chunk 即拒绝单个资产超过 300 MiB按分段大小上限max_segment_size_bytes校验超限报SegmentRejected使数据集资产总数超过 12 个按分段数量上限max_segment_count校验超限报SegmentLimitReached。注Rust 侧注释指出录制与蓝图数据集不受限制上述上限仅针对资产数据集这些数值与云服务器保持一致。Viewer 与 Chunk 处理 API 中的资产可见性当 Rerun Viewer 打开某个分段时会连同加载该数据集的资产与分段自身数据资产数据会被缓存因此打开同一数据集的其他分段时不会重复下载。数据集的 assets 标签页会列出已注册资产及其元数据并支持直接在 Viewer 中注册、注销资产。资产也会出现在Python chunk 处理 API中前提是include_assetsTrue参见 Chunk Processing APIsegment_id dataset.segment_ids()[0] # Covers the chunks of both the segment and the datasets assets. store dataset.segment_store(segment_id) for chunk in store.stream().to_chunks(): print(chunk.entity_path) # Covers only the segment, and no asset manifests are fetched. segment_only dataset.segment_store(segment_id, include_assetsFalse)segment_store(segment_id, include_assetsTrue)返回一个远程分段的LazyStoremanifest 立即获取chunk 数据按需惰性加载LazyStore.stream()如需完整物化可调用lazy.stream().collect()。获取一个资产的 manifest 需要一次网络请求因此当你只关心分段自身数据时include_assetsFalse是更经济的选择。资产不会出现在哪些 API 中以下 API只停留在数据集自身的分段上资产永远不会出现dataframe 查询 API见 Dataframe QueriesDatasetEntry.schema()、segment_ids()、segment_table()、reader()——它们都忽略资产数据集。资产数据集是隐藏的hidden因此在CatalogClient.datasets()中默认也不会出现除非传入include_hiddenTrue。CatalogClient.entries()、tables()同理支持include_hidden参数见 _catalog_client.py。直接查询资产数据要查询资产数据需要直接以资产数据集本身为目标——它是一个普通数据集每个已注册资产对应一个分段asset_dataset dataset.asset_dataset() print(asset_dataset.schema()) # Assets only hold static data, so there is no index to read along. df asset_dataset.reader(indexNone)由于资产只含静态数据reader()的index参数必须传None仅读取静态数据模式这也与上文资产禁止包含时序数据的限制互为印证。附SDK 能力速览与进阶入口客户端CatalogClient(url, tokenNone)连接目录服务器提供datasets()/tables()/entries()可传include_hidden、get_dataset()/get_table()按name或id、create_dataset()/create_table()/register_table()、version_info()、benchmark()RTT 与下载带宽测量、do_global_maintenance()以及ctxDataFusion 会话上下文见 _catalog_client.py数据集查询DatasetEntry.reader(indexNone, …)返回 DataFusion DataFrame且rerun_segment_id与索引列的过滤可下推到服务器端执行filter_segments()/filter_contents()返回支持链式组合的惰性DatasetView实体路径表达式支持/points/**通配与-/text/**排除get_index_ranges()返回各分段各索引的范围元数据 joinsegment_table(join_meta…, join_keyrerun_segment_id)可将分段元数据表与自定义元数据表/DataFrame 左连接例如按rerun_segment_id关联任务成功率等业务字段分段级 URLsegment_url(segment_id, timeline, start, end)可生成指向某分段在指定时间线、时间窗上的直接链接维护DatasetEntry.do_maintenance(optimize_indexes, retrain_indexes, compact_fragments, cleanup_before, unsafe_allow_recent_cleanup)提供索引优化、碎片压缩与旧存储清理等维护操作unsafe_allow_recent_cleanup使用不当会导致数据丢失须谨慎。更完整的 SDK 方法清单与签名可查阅 Catalog SDK 参考文档、SDK 源码 rerun_py/rerun_sdk/rerun/catalog/_entry.py 与 _catalog_client.py以及服务端实现 crates/store_app/re_server/src/store/ 下的dataset.rs、segment.rs、source.rs。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考