Back to Blog

修复 delta-kernel-rs 的隐藏坑:Snapshot::builder_for 支持无尾斜杠 URL(Issue #1765)

Open Source
Rust
Delta Lake
URL
Bugfix
Testing
GitHub

delta-io/delta-kernel-rs 中 Snapshot 构建会用 Url::join 拼接 _delta_log 路径;当 table URL 没有 trailing slash 时会替换最后一段路径导致路径错误。本次通过在 SnapshotBuilder::new_for 里统一补齐尾斜杠并完善测试,避免用户踩坑。

Published on 2/5/2026

这次是一个典型的“看起来没问题、但会在生产里炸”的小坑:Rust 的 Url::join() 语义在 URL 没有 trailing slash 时,会把最后一个 path segment 当作“文件名”来替换,而不是追加目录。

delta-kernel-rs 里,Snapshot 构建会做 table_root.join(\"_delta_log/\")。于是当用户传 s3://bucket/path/to/table(没有 /)时,内部会解析成 s3://bucket/path/to/_delta_log/,把 table 段直接替换掉,最终引发各种“权限/文件不存在”这类非常迷惑的错误。

🔍 分析 (Analyze)

这类问题的“坏”在于:

  • 用户输入在语义上完全合理(很多人不会特意写尾斜杠)。\n
  • 错误表现发生在很后面(读 _delta_log 时才爆),并且报错不直观。\n
  • 一旦是云存储(S3/ABFS/GCS),错误会被包装成权限/路径问题,更难定位。

📍 定位 (Locate)

问题核心是 Snapshot 构建时拼接日志目录:

  • SnapshotBuilder::new_for(table_root) 保存 table root。\n
  • SnapshotBuilder::build() 使用 table_root.join(\"_delta_log/\") 来定位日志目录。

table_root 没有 / 结尾时,Url::join 的行为会替换最后一段路径。

🛠️ 执行 (Execute)

修复策略很直接:在入口处把 table root 统一当作目录

  1. SnapshotBuilder::new_for 里对 table_root 做归一化:如果 path 不以 / 结尾,则补上。\n
  2. 补齐并修正相关测试,让测试数据真实写入到 table_root/_delta_log/... 下。\n
  3. 本地验证:cargo test -p delta_kernel

🔁 跟进 (Follow-up)

这单在 review 阶段有一次代码补丁跟进(2026-02-11):

  • Reviewer 明确要求补 utility function 单测并提升 coverage。
  • 我补了两组测试并推送 32ba66e
    • kernel/src/utils.rsnormalize_table_root_url 的 trailing slash/empty-path 场景。
    • test-utils/src/lib.rsdelta_path_for_version_at 的 root 与 nested table path 场景。
  • 验证命令:
    • cargo fmt --all
    • cargo test -p delta_kernel normalize_table_root_url
    • cargo test -p test_utils delta_path_for_version_at
  • PR 跟进评论:https://github.com/delta-io/delta-kernel-rs/pull/1769#issuecomment-3881621006

✅ 总结 (Summary)

这个修复属于“改动很小,但能显著提升鲁棒性”的典型案例:

  • API 对用户输入更宽容(不用强制 trailing slash)。\n
  • 避免隐藏的路径拼接陷阱,减少线上误判成权限问题的概率。\n
  • 测试覆盖更贴近真实表结构,能更早捕获同类回归。