Lance 表格式版本化完全指南Feature Flags 位图协议、读写兼容性校验与源码实现解析【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance导读Lance 表格式Lance Table Format以不可变的 Manifest 描述每一个数据集版本而格式本身仍在持续演进——新版本会引入删除文件、稳定行 ID、多基路径、数据覆盖文件等新能力。如何在旧读/写器与新旧数据集之间建立安全边界答案就是本文的主题Format Versioning格式版本化。本指南以仓库中的官方规范文档 versioning.md 为主体完整解读reader_feature_flags与writer_feature_flags两套位图标志的语义、未知标志拒绝机制、配对约束并结合 table.proto、feature_flags.rs 等源码给出实现级证据。读完本文你将掌握 Lance 格式兼容性协议的全部 9 个标志位理解为什么未知标志必须拒绝、半置位的清单是非法的并能独立判断某个 Lance 数据集需要何种版本的读/写器。一、为什么需要 Feature Flags格式演进与兼容性的安全阀Lance 表格式将数据集组织为带版本号的片段Fragment、数据文件Data File、删除文件Deletion File与索引Index集合每个版本由一个不可变的 Manifest 描述Manifest 中引用该快照的物理数据见 表格式索引。这种设计天然要求向后可读一个 2024 年写的表2026 年的新版本库应当能打开反过来一个使用新格式特性写出的表旧版本库必须明确拒绝而不是猜着读导致静默的数据错误。Feature Flags特性标志正是这条安全边界的实现载体。格式每引入一种读者/写者需要特殊处理的新特性就在格式中登记一个标志位。核心规则原文表格式中有两个独立的标志字段分别面向读取与写入场景读取方应检查reader_feature_flags确认其中没有自己不认识不支持的标志写入方应检查writer_feature_flags任一方只要发现未知标志就必须在任何读或写操作上返回 unsupported不支持错误而不是继续执行。这样做的逻辑很朴素不认识某个标志 无法兑现该标志所要求的语义 继续操作会产出错误结果。例如忽略删除文件会把已删除的行重新返回给用户忽略覆盖文件会返回过期的旧值。这两个字段在协议层定义于 table.proto 的Manifest消息中table.proto#L36-L149// Feature flags for readers. uint64 reader_feature_flags 9; // Feature flags for writers. uint64 writer_feature_flags 10;它们都是uint64位图第 N 位1 N对应一个命名标志。从源码看Lance Rust 实现把所有标志常量集中定义在 feature_flags.rs从FLAG_DELETION_FILES 1 0一直到FLAG_MIXED_DATA_FILE_VERSIONS 1 8并声明FLAG_UNKNOWN: u64 1 9作为第一个未知位的边界。二、当前全部 Feature Flags 速查表下表完整收录规范文档中的全部标志bit 值为位图数值即1 nFlag BitFlag NameReader RequiredWriter RequiredDescription1FLAG_DELETION_FILESYesYes片段Fragment可能包含删除文件记录软删除行的墓碑tombstone。2FLAG_STABLE_ROW_IDSYesYes行 ID 对移动move与更新update均保持稳定片段内含行 ID 到行地址的索引。4FLAG_USE_V2_FORMAT_DEPRECATEDNoNo数据文件以新版 v2 格式写入。该标志已废弃、不再使用。8FLAG_TABLE_CONFIGNoYesManifest 中存在表级配置table config。16FLAG_BASE_PATHSYesYes数据集使用多个基路径用于浅克隆或多基数据集。32FLAG_DISABLE_TRANSACTION_FILENoYes事务直接记录在 Manifest 中而非单独的 transaction 文件。64FLAG_UNSTABLE_DATA_OVERLAY_FILESYesYes片段可能携带数据覆盖overlay文件。不稳定特性发布构建默认拒绝除非显式开启。128FLAG_COVERED_INDEX_METADATAYesYes存在声明了覆盖列IndexMetadata.covering_fields的索引此时fields表示被键控列 被携带列。不识别该标志的实现按fields成员关系选索引会把仅被携带列的查询错误地交给以另一列为键的索引。256FLAG_MIXED_DATA_FILE_VERSIONSYesYes快照可能引用不同精确版本的、可识别的 V2 数据文件。读写双方必须都置位且后续版本必须保持置位。未知标志与配对约束原文要点位值在512 及以上即1 9起的标志均为未知标志任何实现遇到都会以 unsupported 错误拒绝该数据集而配对的混合数据文件版本位256其读写两位必须同时置位或同时清除半置位的 Manifest 是非法的。三、逐位详解每个标志的含义、触发条件与实现位 1FLAG_DELETION_FILES——软删除墓碑文件当任一片段带有删除文件Deletion File时该位被置位。删除文件以墓碑形式记录被软删除的行偏移无需重写底层数据文件即可实现删除。在 feature_flags.rs 中apply_feature_flags会扫描所有片段let has_deletion_files manifest .fragments .iter() .any(|frag| frag.deletion_file.is_some()); if has_deletion_files { manifest.reader_feature_flags | FLAG_DELETION_FILES; manifest.writer_feature_flags | FLAG_DELETION_FILES; }删除文件的结构定义见 table.proto#L597-L632支持两种存储格式——Arrow IPC 数组.arrow扩展名适合稀疏删除与 Roaring Bitmap.bin扩展名适合稠密删除。删除文件的路径形如{root}/_deletions/{fragment_id}-{read_version}-{id}.{extension}。因为读取时必须过滤被删除行所以读写双方都必须认识该标志。位 2FLAG_STABLE_ROW_IDS——稳定行 ID该位表示行 ID 对移动与更新都保持稳定片段内包含行 ID 到行地址的映射索引。它让诸如merge_insert、update等重写片段的操作可以不改变行的逻辑标识。实现上的强制约束很有意思如果任一片段带行 ID则所有片段都必须带行 ID否则apply_feature_flags直接返回非法输入错误feature_flags.rs#L102-L116let has_row_ids manifest.fragments.iter().any(|frag| frag.row_id_meta.is_some()); if has_row_ids || enable_stable_row_id { if !manifest.fragments.iter().all(|frag| frag.row_id_meta.is_some()) { return Err(Error::invalid_input(All fragments must have row ids)); } manifest.reader_feature_flags | FLAG_STABLE_ROW_IDS; manifest.writer_feature_flags | FLAG_STABLE_ROW_IDS; }Manifest 还通过 table.proto#L182 的next_row_id字段记录下一个未使用的行 ID注释明确指出仅在设置了 stable_row_ids 特性标志时使用。行 ID 的完整语义见 行 ID 与血统规范。位 4FLAG_USE_V2_FORMAT_DEPRECATED——已废弃的 v2 格式标志历史上该位标记数据文件以 v2 格式写入。如今 v2 格式已是常态此标志不再使用读写双方都不要求。源码中保留常量仅为兼容读取feature_flags.rs#L16并提供has_deprecated_v2_feature_flagfeature_flags.rs#L244-L246用于探测旧清单中的该位。位 8FLAG_TABLE_CONFIG——表级配置当 Manifest 中存在表配置configmap时置位且仅写入方需要识别。原因在于表配置告诉库如何读写与管理表写入方必须遵守其中的配置而读取方不需要执行配置策略。实现位于 feature_flags.rs#L119-L121if !manifest.config.is_empty() { manifest.writer_feature_flags | FLAG_TABLE_CONFIG; }。配置键的命名约定见 table.proto#L206-L212以lance.为前缀的键保留给 Lance 库本身其他库也应使用自己的前缀避免冲突。位 16FLAG_BASE_PATHS——多基路径当数据集使用多个基路径时置位典型场景是浅克隆shallow clone与多基数据集——数据文件可能位于当前数据集根目录之外如其他桶、其他目录。Manifest 中的base_paths列表table.proto#L241-L261配合数据文件与删除文件上的base_id字段解析真实路径base_paths[id 0] /data/ file.pathapply_feature_flags在base_paths非空时对读写双方同时置位feature_flags.rs#L124-L127。对应测试 test_base_paths_feature_flags 验证了普通数据集不置位带 base_paths 的数据集双置位。多位置存储的整体规则见 存储布局规范。位 32FLAG_DISABLE_TRANSACTION_FILE——内联事务该位仅在写入方要求表示事务直接记录在 Manifest 中而非单独的 transaction 文件。Manifest 协议里对应两个字段table.proto#L170 的transaction_file路径格式{read_version}-{uuid}.txn可为空与 table.proto#L176 的transaction_sectionManifest 文件内联的事务内容位置。当写入方选择内联事务disable_transaction_file参数为真时置位feature_flags.rs#L141-L143。事务提交协议与冲突解决的完整说明见 事务规范。位 64FLAG_UNSTABLE_DATA_OVERLAY_FILES——数据覆盖文件不稳定特性数据覆盖文件Data Overlay File为片段中一小部分单元格提供新值而无需重写底层数据文件使小范围更新变得廉价。该位要求读写双方都必须理解——因为读取时若不处理覆盖层会静默返回过期的基值。这是目前唯一带不稳定前缀的标志其门控策略在源码中体现得淋漓尽致FLAG_UNSTABLE_DATA_OVERLAY_FILES是未知边界内的最后一个已知位且发布构建release默认把它当作未知标志拒绝除非设置环境变量LANCE_ENABLE_UNSTABLE_DATA_OVERLAY_FILES显式开启调试构建debug始终理解该标志以便测试覆盖此路径。对应实现feature_flags.rs#L170-L200pub const ENABLE_UNSTABLE_DATA_OVERLAY_FILES_ENV: str LANCE_ENABLE_UNSTABLE_DATA_OVERLAY_FILES; fn data_overlay_files_enabled() - bool { cfg!(debug_assertions) || std::env::var_os(ENABLE_UNSTABLE_DATA_OVERLAY_FILES_ENV).is_some() }apply_feature_flags在存在 overlay 时对读写双方置位feature_flags.rs#L132-L139supported_flags_when则按环境决定是否把该位从受支持集合中移除。DataOverlayFile的完整协议覆盖位图、稠密/稀疏布局、committed_version排序、索引集成与压实见 数据覆盖文件规范。位 128FLAG_COVERED_INDEX_METADATA——覆盖列索引元数据这是语义最微妙的一个标志。Lance 索引可以在IndexMetadata.covering_fields中声明覆盖列索引除了自身键控的列还顺带携带若干列的值使只投影这些列的查询可以完全由索引回答免去对基表的回表。问题在于covering_fields声明之后IndexMetadata.fields的含义从该索引被搜索的列变为键控列 被携带列。一个不认识该标志的旧实现作为读取方仍按fields成员关系选择索引会把仅被携带列的查询交给以另一列为键的索引返回错误的近邻且不报任何错作为写入方会把fields中每一项都当作键控列来维护索引维护在错误的依赖集上。因此读写双方都必须拒绝这类数据集。该位是从已退役的 MemWAL 索引追赶标志回收而来边界恰好落在当时已发布构建的未知边界上从而旧构建无需改动即可自然拒绝含此位的数据集但 v11.0.0-beta.4 至 beta.17 窗口内的构建仍会将其视为受支持而放行覆盖数据集。源码注释与栅栏断言详见 feature_flags.rs#L33-L52相关索引元数据校验如拒绝covering_fields长于fields见 index.rs。位 256FLAG_MIXED_DATA_FILE_VERSIONS——混合数据文件版本正常情况下一个快照内的所有数据文件应使用同一个存储版本data_format.version见 table.proto#L184-L204。而该位声明快照可以引用不同精确版本的、可识别的 V2 数据文件此时每个DataFile的file_major_version/file_minor_version各自权威见 table.proto#L508-L515。它的两个特殊约束源码级实现配对约束paired读写两个位必须同时置位或同时清除。validate_paired_feature_flagsfeature_flags.rs#L254-L265会检查半置位状态并返回CorruptFile错误Manifest has only one of the mixed>fn supported_flags_when(overlay_enabled: bool) - u64 { let mut supported FLAG_UNKNOWN - 1; // 所有已知位 mark_supported(mut supported, FLAG_UNSTABLE_DATA_OVERLAY_FILES, overlay_enabled); supported } pub fn can_read_dataset(reader_flags: u64) - bool { reader_flags !supported_flags() 0 } pub fn can_write_dataset(writer_flags: u64) - bool { writer_flags !supported_flags() 0 }FLAG_UNKNOWN - 1即所有已知位的集合只要清单中的标志集合存在本构建不认识的位与!supported_flags()按位与非零判定即失败。具体的拒绝动作由ensure_can_read_manifest/ensure_can_write_manifest完成feature_flags.rs#L212-L242错误信息会引导用户升级This dataset cannot be read by this version of Lance. Please upgrade Lance to read this dataset. Flags: {flags}配对约束的校验逻辑半置位清单非法位于 feature_flags.rs#L254-L265在每次读取、写入、提交前都会执行——注意它只校验、不自动修复因为一个位置位意味着某个旧读/写器仍被允许这不是任何一种可被归一化的模式。五、源码实现标志的生成与校验全链路5.1 写盘前自动计算apply_feature_flags每次构造 Manifest 并落盘前feature_flags.rs#L73-L151 的apply_feature_flags会根据 Manifest 实际内容自动重算两个标志字段扫描片段判断是否存在删除文件/行 ID/overlay检查config与base_paths是否非空再按参数决定是否置事务内联位。计算完成后把重算的covered_index_metadata与粘性配对位重新合并回去防止二次调用时被重置丢失。5.2 读取准入打开数据集即校验打开一个数据集时第一道闸门就是读取准入检查builder.rs#L872-L874在load_by_uri加载 Manifest 后立即调用ensure_can_read_manifestdataset.rs#L799、dataset.rs#L880缓存命中时、dataset.rs#L1241 等处同样执行读取后还会调用check_manifest_storage_versionversions/mod.rs#L319做存储版本契约校验。5.3 写入准入提交与增量写都需放行写入路径上的闸门包括commit.rs#L425、commit.rs#L1160、commit.rs#L1498-L1514提交commit前对源清单与待写清单执行ensure_can_write_manifestinsert.rs#L353增量写入时检查can_write_dataset(dataset.manifest.writer_feature_flags)dataset.rs#L3325例如在基于源数据集创建新数据集如 clone时先确保源清单可写。5.4 存储版本契约混合版本位的联动check_manifest_storage_contractversions/mod.rs#L338-L462把混合版本位与数据文件版本强关联若混合位启用但默认存储版本是 V1直接报错若存在与默认版本不一致的数据文件而混合位未启用读取时拒绝、最终提交Finalize时自动为读写双方补上FLAG_MIXED_DATA_FILE_VERSIONS位versions/mod.rs#L455-L456。相关集成测试update 路径见 update.rs#L805、merge_insert 路径见 merge_insert.rs#L4469验证了混合版本场景下标志的置位与保留行为。六、测试验证单元测试如何守护位图协议feature_flags.rs 内置了一整套针对位图协议的单元测试可作为理解协议的可执行文档test_read_check/test_write_check验证所有已知位均可读可写而FLAG_UNKNOWN必须被拒绝test_data_overlay_flag_release_gating验证发布构建默认把 overlay 位视为未知、开启环境变量后放行test_base_paths_feature_flags验证普通数据集与多基数据集在FLAG_BASE_PATHS上的差异test_apply_feature_flags_sets_overlay_flag验证带 overlay 的片段会触发双置位一组 paired/sticky 测试inheriting_carries_sticky_paired_bits_from_the_source、inheriting_refuses_a_half_set_source、apply_feature_flags_rejects_half_set_sticky_bits、paired_validation_rejects_half_set_mixed_version_capability覆盖半置位清单被拒绝、粘性位被继承的全部分支test_covered_index_metadata_fences_older_builds_only用断言锁定覆盖索引位必须恰好位于旧发布构建的未知边界128确保老构建天然拒绝、新构建放行的栅栏语义不漂移。七、版本化相关的其他规范速览Format Versioning 只是 Lance 表格式规范矩阵中的一环与之紧密相关、可在当前仓库继续深入阅读的规范包括表格式索引Manifest、片段、数据文件、删除文件的总览存储布局规范文件组织、基路径系统与多位置存储事务规范MVCC、提交协议、事务类型与冲突解决行 ID 与血统规范行地址、稳定行 ID、行版本追踪数据覆盖文件规范位 64 对应的完整格式说明分支与标签基于版本的分支管理索引格式向量、标量与全文索引格式。掌握这套 Feature Flags 协议你就掌握了 Lance 格式向前兼容的底层机制位图声明能力、未知即拒绝、配对与粘性约束三者共同保证了无论格式如何演进旧工具永远不会在它无法正确理解的数据上猜着工作。【免费下载链接】lanceOpen Lakehouse Format for Multimodal AI. Convert from Parquet in 2 lines of code for 100x faster random access, vector index, and data versioning. Compatible with Pandas, DuckDB, Polars, Pyarrow, and PyTorch with more integrations coming..项目地址: https://gitcode.com/GitHub_Trending/la/lance创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考