Skip to content

TSI-4327: fix(db): 修复跨父目录移动后投影仍留在旧位置 - #166

Merged
tsic404 merged 1 commit into
mainfrom
multica/TSI-4327
Oct 25, 2026
Merged

tsic404 merged 1 commit into
mainfrom
multica/TSI-4327

Conversation

@multica-tsic404

@multica-tsic404 multica-tsic404 Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes TSI-4327

Issue: https://multica.tsic.top/issues/01a17488-d8e2-777d-b70d-11f5562588e3

设计依据

tsic/torrentfs-design@5b622b6

设计实现

设计要求 代码落点
02-contracts §C-3.3.6「该目录自身」:改名目录时同步更新该目录自身 src/infrastructure/db/metadata_ops.rs:85 · 同一事务内按新路径的父路径解析 metadata_directories.id,与 name/path 一并写入 parent_id(移到根目录时置空)
02-contracts §C-3.3.6「子目录路径」 src/infrastructure/db/metadata_ops.rs:107 · 前缀替换更新全部后代 path(既有语句未改动,新测试覆盖其与新父链接的一致性)
02-contracts §C-3.3.6「精确匹配的来源路径」 src/infrastructure/db/metadata_ops.rs:113 · torrent_sources.source_path = old_path 的整行更新(既有语句)
02-contracts §C-3.3.6「前缀匹配的来源路径」 src/infrastructure/db/metadata_ops.rs:119 · 前缀替换更新子树来源路径(既有语句)
02-contracts §C-3.3.6:目录闭包表不重建(标识未变) 未改动 metadata_directory_closure(落库语句只涉及 metadata_directories 与 torrent_sources)
03-acceptance §A AC-04 / TC-ingest-04:跨目录移动旧位置消失、新位置出现;移动到不存在的目录非 0 退出且两侧投影不变 tests/acceptance/test_tc_ingest_04.rs(tests/acceptance/main.rs:38,135 注册);落库层回归 src/infrastructure/db/tests.rs:1512
01-scope 禁项 F1:data/ 接受变更操作 未引入(改动只在 metadata/ 改名落库、DB 测试与验收载体,data/ 无写入口)

验证

  • cargo test 全量(rebase 到 main 的 20a627d 之后,含新并入的 negative 载体):642 passed, 0 failed, 5 ignored。
  • 反向验证(只留测试、把落库修复 stash 掉):tc_ingest_04_cross_parent_move_swaps_both_projections 在「the old location must stop listing the moved directory」失败;test_rename_metadata_directory_across_parents_reparents 在「the old parent must no longer list the moved directory」失败。恢复修复后两者通过。
  • 断言在改名后立即读取,不等待 entry TTL:该缺陷的来源是 get_source_path_prefixes 按 parent_id 枚举子项,与内核缓存无关,故本修复不依赖另一个改名的缓存失效通道,两者可独立成立。
  • PR 已 rebase 到 main(20a627d),冲突仅在 tests/acceptance/main.rs 的模块/注册表插入点:两边的模块声明与 LANDED 条目全部保留(negative 01–04 + ingest-04)。

@sourcery-ai

sourcery-ai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Sorry @multica-tsic404[bot], you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 14 hours and 30 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

Fix cross-parent metadata directory moves by updating the moved directory’s parent_id alongside its path, so data/ projections immediately disappear from the old parent and appear under the new parent while descendants and torrent contents remain intact. The PR adds database persistence tests and an acceptance test covering successful moves, missing-parent failures, root moves, and reopen behavior.

Sequence diagram for cross-parent metadata directory move

sequenceDiagram
    participant Client
    participant Database
    participant MetadataDirectories
    participant TorrentSources

    Client->>Database: rename_metadata_directory(old_path, new_path, new_name)
    Database->>MetadataDirectories: SELECT id WHERE path = parent_path
    MetadataDirectories-->>Database: new_parent_id
    Database->>MetadataDirectories: UPDATE name, path, parent_id
    Database->>MetadataDirectories: UPDATE descendant paths
    Database->>TorrentSources: UPDATE exact and prefixed source_path values
    Database-->>Client: commit transaction
Loading

Entity relationship diagram for metadata directory reparenting

erDiagram
    metadata_directories {
        INTEGER id PK
        TEXT path
        TEXT name
        INTEGER parent_id FK
    }
    torrent_sources {
        TEXT source_path
    }
    metadata_directories ||--o{ metadata_directories : parent_of
    metadata_directories ||--o{ torrent_sources : projects
Loading

File-Level Changes

Change Details Files
Reparent the renamed metadata directory while updating its path, descendants, and torrent source paths in one transaction.
  • Resolve the destination parent by the new path and persist its ID, clearing it for root moves.
  • Retain existing exact-path and subtree prefix updates for descendant directories and torrent sources.
  • Leave closure-table rows unchanged because directory identities are preserved.
src/infrastructure/db/metadata_ops.rs
Add persistence-level regression coverage for cross-parent moves and root moves.
  • Verify old and new parent listings, descendant paths, torrent paths, and behavior after reopening the database.
  • Verify moving back to the root clears the parent link without deleting directory rows.
src/infrastructure/db/tests.rs
Add acceptance coverage proving metadata moves immediately swap both data projections.
  • Generalize torrent ingestion to target arbitrary metadata parents and source paths.
  • Test missing destination failure preserves both projections and returns ENOENT.
  • Test successful cross-parent moves remove the old projection, create the new one, and preserve nested directories and torrent contents.
  • Register the new TC-ingest-04 acceptance case.
tests/acceptance/common.rs
tests/acceptance/main.rs
tests/acceptance/test_tc_ingest_04.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

`data/` 投影按 `parent_id` 枚举目录的子项,而改名只更新了名称、路径与
来源路径。跨父目录移动后父链接仍指向旧父目录,子树无论重读多少次都留在
旧位置,重新挂载还会把它恢复到旧父目录下。

改名现在在同一事务内把父链接指向新父目录路径对应的行(移动到根目录时
置空)。旧位置立即不再列出该目录,新位置立即列出,重挂载后层级保持。

@tsic404 tsic404 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查摘要(APPROVED,零发现)

  1. 规模:L2 — 245 insertions / 9 deletions,5 文件 · 类型:Bug 修复(落库层父链接)
  2. 覆盖维度:代码质量 ✓ | 安全 ✓ | 架构 ✓ | 规范 ✓ | 性能 ✓ | 并发 ✓ | API 兼容 ✓ | 测试判别力 ✓ | Rust 专项 ✓
  3. 其他审查者反馈:无(reviews 为空,无 CHANGES_REQUESTED 需承接)
  4. 无法锚定的发现:无

设计依据:tsic/torrentfs-design@5b622b6(等于 issue 冻结锚点 design_anchor)

核对结论

  1. parent_id 解析(src/infrastructure/db/metadata_ops.rs:85)覆盖两种边界:「移到根目录」→ rsplit_once 返回 None → parent_id = NULL;「目标父目录不存在」→ 该分支在 DB 层取不到行时会落为 None,但路径不可达:FsService::rename 在进入服务层前按 inode 存在性返回 ENOENT(src/fuse/fs_service.rs:1501-1505),验收用例失败面 tests/acceptance/test_tc_ingest_04.rs:72-91 断言 ENOENT 且两侧投影不变。
  2. 与同一事务内 path 前缀替换一致:父链接在步骤 1(:85)写入,前缀替换在步骤 2(:107);合法改名下被移动行在步骤 1 后已不匹配 old_path/,两者不交叉;后代 parent_id 不变(标识不变),故 metadata_directory_closure 不重建的前提成立——该表全仓只有建表/索引与 INSERT,无 SELECT 读路径,无可观察影响。
  3. ## 设计实现 七行逐行对 diff:均有支撑(明细见 issue 评论的契约符合性核对表)。

点赞

  1. 父链接与 name/path 同事务、同一条 UPDATE 写入,metadata_directories 行自洽(path 蕴含的父路径与 parent_id 一致);挂载恢复 restore_metadata_inodes 正是按 parent_id 建树(src/fuse/inodes.rs:415-419),因此修复同时堵住「重挂载回落旧父目录」。
  2. rsplit_once('/') 用 None 表达根级,不构造空路径再查库;.optional()? 让 DB 错误上抛,未引入 unwrap。
  3. 两侧测试都能失败(作者已做反向验证):DB 测试断言旧父为空、新父单条、自身子项不变并重开库(src/infrastructure/db/tests.rs:1512);验收测试同时压失败面与成功面,改名后立即读取,不靠 entry TTL 过期(readdir 快照键为 total_changes,见 src/fuse/lookup.rs:501)。

@tsic404
tsic404 merged commit 7d96057 into main Oct 25, 2026
15 checks passed
@tsic404
tsic404 deleted the multica/TSI-4327 branch October 25, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant