Skip to content

Shrink the hashfile: index file uniqueness by path hash - #2

Merged
martinus merged 1 commit into
masterfrom
prototype/path-hash-index
Jul 15, 2026
Merged

Shrink the hashfile: index file uniqueness by path hash#2
martinus merged 1 commit into
masterfrom
prototype/path-hash-index

Conversation

@martinus

Copy link
Copy Markdown
Owner

Summary

The files table enforced UNIQUE(filename), whose automatic index stores a second full copy of every path. On a 20k-file tree that index was 1.75 MB — 24% of the whole hashfile — and it exists only to keep one row per path (for the INSERT OR REPLACE upsert, rename, and -R removal). None of those need the path text.

This stores a 64-bit csum_path(filename) alongside the path and enforces UNIQUE(path_hash) instead. The path text is still kept (files must be opened by name to dedupe), but the uniqueness index now costs 8 bytes/row. The delete-by-path query keeps a filename tie-breaker, so a hash collision can't remove the wrong row; at worst a collision causes one unrelated file to be rescanned, never data loss. The hashfile minor version is bumped so older files are rejected cleanly.

Measurements (vacuumed hashfile)

Path length master this PR Saving
short (~3–4 char) 2.35 MB 2.09 MB −11%
typical (~68 char) 6.69 MB 5.62 MB −16%
long (~130 char) 4.26 MB 3.06 MB −28%

The UNIQUE index itself shrinks ~79%. Scan speed is unchanged (within 1%, measured interleaved).

Testing

All unit and integration tests pass, plus new test_pathhash.py covering path-reuse eviction and -R removal — these pass identically on this branch and on stock master, confirming behavior is preserved. The version guard rejects cross-version hashfiles cleanly in both directions.

🤖 Generated with Claude Code

The files table enforced UNIQUE(filename), whose automatic index stores a
second full copy of every path. Measured on a 20k-file tree that index was
1.75 MB - 24% of the whole hashfile - and it exists only to keep one row per
path (for the INSERT OR REPLACE upsert, rename, and -R removal); nothing
needs the path *text* for those.

Store a 64-bit csum_path(filename) alongside the path and enforce
UNIQUE(path_hash) instead. The path text is still kept (files must be opened
by name to dedupe), but the uniqueness index now costs 8 bytes/row. The
upsert, rename and delete-by-path queries key on the hash (delete keeps a
filename tie-breaker so a hash collision can't remove the wrong row); a
collision could at worst cause one unrelated file to be rescanned, never data
loss. Bump the hashfile minor version so old files are rejected cleanly.

Hashfile size (vacuumed): -11% short paths, -16% typical (~68 char),
-28% long paths (~130 char); the UNIQUE index itself shrinks ~79%. Scan
speed is unchanged. All unit and integration tests pass, plus new
test_pathhash.py covering path-reuse eviction and -R removal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhuZfCNECmgdb8FozGKagi
@martinus
martinus merged commit 2312cd9 into master Jul 15, 2026
2 checks passed
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