Skip to main content

Module cache

Module cache 

Source
Expand description

Persistent on-disk cache for Phase K.

The cache stores a serialized FileIndex per PHP file, keyed on (uri, content). On a warm start scan_workspace reads the cached index instead of parsing the file, shrinking cold-start I/O from O(parse) to O(read + bincode-decode) — roughly 10–50× faster per file.

§Layout

~/.cache/php-lsp/<schema-version>/<workspace-hash>/<uri-hash>.bin
  • <schema-version> — php-lsp crate version; bumping it rotates the entire cache so old entries are never decoded against a newer schema. The previous version’s directory is pruned by the next WorkspaceCache::new call rather than left to accumulate release over release.
  • <workspace-hash> — blake3 of the canonicalized absolute path of the first workspace root, truncated to 16 hex chars. Two separate projects get isolated caches; two checkouts of the same project at the same absolute path share one. Holds a workspace.json recording the root path and last-used time, so the background pruner can drop caches for roots that no longer exist or haven’t been opened in [WORKSPACE_MAX_IDLE].
  • <uri-hash> — blake3 of the URI, truncated to 32 hex chars. One file per URI, not per (uri, content) pair: a content hash is stored inside the entry and checked on read, so re-indexing an edited file overwrites its existing slot in place instead of leaving the previous revision’s entry to rot on disk.

<workspace-hash> also holds a session/ subdirectory: mir’s own AnalysisCache, nested here (not under mir’s default .mir-cache/) so both caches share schema/workspace rotation and cleanup. size_bytes recurses into it for the size cap.

§Format

bincode v2 (binary, fast, schema-stable via serde derives on FileIndex et al), prefixed with the entry’s content hash so a stale revision reads back as a miss. Files are written atomically via a temp-file rename to avoid half-written entries on an interrupted shutdown.

§Invalidation

Rotating the schema version invalidates everything; rotating the content invalidates just that entry’s slot (overwritten on next write). The size cap in WorkspaceCache::new is a backstop for workspaces that outgrow it, not the primary cleanup mechanism.

Structs§

CacheKey
Identifies a single cache entry. Opaque — callers produce it via WorkspaceCache::key_for and pass it straight back to read/write.
WorkspaceCache
Handle to the cache directory for a single workspace. Construction is cheap (creates directories on demand); the same handle can be shared across threads via Arc — it holds no mutable state.

Constants§

CACHE_SIZE_CAP
Size cap (bytes) for a single workspace’s cache directory. At startup, if the directory exceeds this, we reset it — simpler than LRU eviction and the rebuild cost is bounded (it’s just the next workspace scan running as if cold). 512 MiB fits a mega-workspace (50 k files × ~10 KB average FileIndex) with headroom and is small enough that no reasonable disk will choke on it.
TOTAL_CACHE_SIZE_CAP
A workspace cache unused for this long is deleted on the next startup. Cap (bytes) on all workspace caches together. Past it, the least recently used workspace directories are deleted until the total fits again.