Skip to content

[Feature Request] Prefix-shard the semantic cache directory (cache/semantic/ab/<hash>.json) — the flat layout puts ~20k entries in one directory at monorepo scale #1709

Description

@gkaganas-mparticle

Version: verified against current v8 (0.9.8). Env: committed-cache
workflow (semantic cache in git so fresh worktrees/CI warm-start), monorepo
corpus that produces ~20k semantic cache entries.

Problem

The semantic cache layout is flat — every entry is
cache/semantic/{sha256}.json in a single directory. On v8 today:

  • load_cachedentry = cache_dir(root, kind) / f"{h}.json"
    (cache.py:364)
  • save_cachedentry = target_dir / f"{h}.json" (cache.py:409)
  • cached_files / clear_cache — the kind/pattern table uses
    ("ast", "**/*.json"), ("semantic", "*.json") (cache.py:443–445,
    457–460): AST is already globbed recursively (it has per-version
    subdirs), semantic is non-recursive by construction.
  • prune_semantic_cachesemantic_dir.glob("*.json") (cache.py:491),
    also non-recursive.

That's fine at hundreds of entries. At monorepo scale it degrades on both
axes we care about:

The cache being content-hash-keyed makes the fix trivially cheap: the key
already starts with uniformly distributed hex.

Proposal

Two-hex prefix subdirectories, the same scheme git itself uses for loose
objects:

cache/semantic/ab/abcd1234….json     # 256 dirs, ~80 entries each at 20k scale
  1. Write path (save_cached): write to cache_dir(...)/h[:2]/f"{h}.json"
    (mkdir the prefix dir on demand).
  2. Read path (load_cached): try the sharded path first, fall back to the
    legacy flat path — existing caches keep hitting without a migration step,
    and entries migrate organically as content changes (or via an optional
    one-shot graphify cache-migrate).
  3. Enumeration: change the semantic pattern from *.json to **/*.json
    in cached_files / clear_cache (a two-character change in the existing
    kind/pattern table — AST already does exactly this), and switch
    prune_semantic_cache to rglob/**/*.json so pruning sees sharded
    entries. The .tmp atomic-write temporaries stay colocated with their
    entry (same prefix dir), preserving the same-filesystem rename guarantee.
  4. AST cache: unaffected (already namespaced under ast/v{N}/; it's
    machine-local and version-swept, so tree-churn pressure doesn't apply).
    Sharding it too would be consistent but isn't needed for this problem.

No key change, no format change, no version bump semantics: the entry's
identity is still the content hash; only its directory changes.

Why we hit this

We commit graphify-out/cache/semantic/ so every fresh clone/worktree/CI job
warm-starts extraction (a warm update re-extracted 1 file where a cold run
extracts 207 — the economics are in #1710). Committing works
beautifully at small scale; the flat directory is the first thing that breaks
when the corpus is a monorepo. We are adopting exactly the design above
downstream (sharded write + flat-fallback read + recursive globs).

Offer

Happy to contribute the PR: the four call-site changes above plus tests
(sharded round-trip, flat-legacy fallback hit, cached_files/clear_cache/
prune_semantic_cache seeing both layouts, atomic-rename same-dir property).
It's deliberately small and independent of the larger update-conductor RFC.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions