ruvnet / ruvnet/RuVector

Storage pools key on non-canonicalized PathBuf — two spellings of one file produce two Database::create calls and a redb lock error

Open
#932 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
4.5k
Forks
603
Avg merge
23h 32m
Merged PRs (30d)
59

Description

Defect

Both redb connection pools — ruvector-core/src/storage.rs and ruvector-graph/src/storage.rs (the latter now on the Weak+Drop shape after #907) — key their pool map on a PathBuf that is absolutized (joined onto cwd when relative) but never canonicalized. Two spellings of the same file resolve to two slots:

  • a/b/../b/x.db vs a/b/x.db
  • a symlinked directory vs its target
  • on macOS, differing case on a case-insensitive filesystem

Each slot independently calls Database::create against the same underlying file; the second open fails with redb's "Database already open. Cannot acquire lock" — the exact error the pool exists to prevent.

This was noted in #907's tail ("Related, pre-existing in both pools") and is split out here so it survives #907 closing. It predates #902 in ruvector-core as well.

Suggested fix

Canonicalize before keying, in both pools:

  1. The parent directory always exists by the time the key is formed (both pools create_dir_all it first), so path.parent().canonicalize()?.join(file_name) is total for the create-if-absent case — Path::canonicalize on the full path fails when the DB file doesn't exist yet, so canonicalize the parent, not the file.
  2. Keep the traversal check on the user-supplied path (it exists to reject intent, and canonicalizing first would erase the evidence), and use the canonical form only as the pool key + Database::create argument.
  3. Regression tests, same in both crates: open via dir/x.db and dir/sub/../x.db concurrently — must share one Database (no lock error, both handles see each other's writes); open via a symlinked dir and the real dir — same assertion.

Since the two pools are intentionally copies of one reference shape (ADR-340 invariant 3), fix them in the same PR so they don't drift again — or better, extract the pool into a small shared module both crates use, which #907 stopped short of doing.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading ruvector-core/src/storage.rs and ruvector-graph/src/storage.rs, including their directory creation, traversal checks, pool-key construction, and the Weak+Drop shape noted for the graph pool. Run the existing storage tests, then add the issue’s concurrent equivalent-path and symlink-path regression cases in both crates. Done means both spellings share one Database, see each other’s writes, and do not produce a redb lock error.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.