Skip to content

perf(deeplake-fs): serve whole-session reads from the local event cache#329

Merged
khustup2 merged 1 commit into
mainfrom
fix/deeplake-fs-incremental-session-read
Jul 24, 2026
Merged

perf(deeplake-fs): serve whole-session reads from the local event cache#329
khustup2 merged 1 commit into
mainfrom
fix/deeplake-fs-incremental-session-read

Conversation

@khustup2

@khustup2 khustup2 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

The VFS whole-session read (readFile / readFileBuffer in src/shell/deeplake-fs.ts) issued an unbounded SELECT message FROM sessions WHERE path = … ORDER BY creation_date ASC, re-materializing the entire fat message column on every fresh process. On mega-sessions (thousands of rows, tens of MB) that's a multi-second cold read.

Observed in prod: an org whose sessions are re-read repeatedly showed 6+ s per whole-session read (one 2,204-msg / 8.6 MB session), dominating its p95. This is the path grep-interceptor/cat and skillify hit, and it was not covered by the earlier wiki-worker DB-fallback bounding — that fix bounded a different path.

Fix

The capture hook already appends every event to a local per-session cache (session-event-cache.ts, ~/.claude/hooks/session-cache/<sessionId>.jsonl, row-for-row identical to the message column). The wiki-workers already prefer it. This wires the same cache-first strategy into the VFS read:

  • Recover the sessionId from the VFS path (last _-segment of the filename stem — a UUID, so unambiguous even when user/org/ws contain underscores).
  • Read the local cache first; fall back to the full ordered DB read on any miss (session captured on another machine, cache disabled/pruned, or unreadable).
  • readFile and readFileBuffer now share one loadSessionMessages helper.

The DB fallback stays unbounded on purpose — the VFS needs the whole session (cat/grep), unlike the wiki-worker's bounded tail read.

Why it's safe

  • Content is provably identical whether served from cache lines or DB rows: both flow through joinSessionMessagesnormalizeContent, which JSON.parses each row and rebuilds output from parsed fields — so jsonb re-serialization (reordered keys/whitespace) vs the cached string cannot diverge.
  • The cache is a strict optimization: any miss falls back to today's exact DB read. Honors the existing HIVEMIND_SESSION_EVENT_CACHE opt-out and 14-day TTL prune.

Tests

6 new cases in tests/claude-code/deeplake-fs.test.ts: cache-hit avoids the fat read, cache/DB content equivalence, null-miss and empty-cache fall back to DB, readFileBuffer also prefers cache, and sessionId recovery with underscores. Full suite: 90/90 in the deeplake-fs suite, tsc --noEmit clean. (The two graph-on-stop-bundle failures are pre-existing on origin/main — a missing built-bundle artifact, unrelated.)

Summary by CodeRabbit

  • Performance

    • Session file reads now use locally cached session events first, improving responsiveness and reducing unnecessary backend reads.
    • Reads automatically fall back to stored session data when the local cache is unavailable or empty.
  • Bug Fixes

    • Improved session path handling for names containing underscores.
    • Missing or empty sessions now return a clear not-found error.

The VFS whole-session read (readFile/readFileBuffer) issued an unbounded
SELECT message FROM sessions WHERE path = ... ORDER BY creation_date ASC,
re-materializing the entire fat message column on every fresh process. On
mega-sessions (thousands of rows, tens of MB) that is a multi-second cold
read, and it is the dominant driver of fat-payload p95 for orgs whose
sessions are re-read repeatedly (observed 6+ s per read).

The capture hook already appends every event to a local per-session cache
(session-event-cache.ts, row-for-row identical to the message column); the
wiki-workers already prefer it. Wire the same cache-first strategy into the
VFS read: recover the sessionId from the path and read the local cache
first, falling back to the full ordered DB read on any miss (session
captured elsewhere, cache disabled/pruned, unreadable). Both readFile and
readFileBuffer now share one loadSessionMessages helper.

Content is provably identical: both cache lines and DB rows flow through
joinSessionMessages -> normalizeContent, which JSON.parses each row and
rebuilds output from parsed fields, so jsonb re-serialization vs the cached
string cannot diverge. The DB fallback stays unbounded because the VFS needs
the whole session (cat/grep), unlike the wiki-worker's bounded tail read.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Session file reads now use a cache-first loader with ordered database fallback, shared reconstruction, and ENOENT handling for empty sessions. Tests cover cache hits, misses, empty caches, equivalent output, and underscore-containing path segments.

Changes

Session read caching

Layer / File(s) Summary
Cache-first message loading
src/shell/deeplake-fs.ts
Session IDs are parsed from VFS paths, cached events are preferred, and sessionsTable messages provide an ordered fallback.
Session file read integration and validation
src/shell/deeplake-fs.ts, tests/claude-code/deeplake-fs.test.ts
readFile and readFileBuffer share cache-first loading, handle empty sessions with ENOENT, and test cache and database paths including underscore-containing segments.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: efenocchi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning It has useful details, but it does not follow the required template sections for Summary, Version Bump, and Test plan. Reformat the PR description to include the template headings and add an explicit version-bump status plus a checklist-style test plan.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: serving whole-session reads from the local event cache for deeplake-fs.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deeplake-fs-incremental-session-read

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🟢 Lines 98.78% (🎯 90%) 487 / 493
🟢 Statements 96.30% (🎯 90%) 572 / 594
🟢 Functions 97.26% (🎯 90%) 71 / 73
🟢 Branches 90.00% (🎯 90%) 432 / 480
File Coverage — 1 file changed
File Stmts Branches Functions Lines
src/shell/deeplake-fs.ts 🟢 96.3% 🟢 90.0% 🟢 97.3% 🟢 98.8%

Generated for commit be6f7f2.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/claude-code/deeplake-fs.test.ts (1)

879-887: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Match the expected SQL exactly.

includes("SELECT message FROM") accepts a query with a missing path predicate, ordering, or wrong table, allowing fallback tests to pass despite a broken DB-read contract. Match the expected bootstrap and session-read SQL strings and reject unexpected queries.

As per path instructions, tests/** should “Prefer asserting on specific values (paths, messages) over generic substrings.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/claude-code/deeplake-fs.test.ts` around lines 879 - 887, Update the
mock query handler in the affected test to compare the complete expected
bootstrap and session-read SQL strings, including the correct table, path
predicate, and ordering, instead of using the generic `includes("SELECT message
FROM")` check. Return the configured rows only for those exact queries and
reject unexpected SQL; assert specific paths and messages where the test
validates results.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/shell/deeplake-fs.ts`:
- Around line 96-108: Update sessionIdFromVfsPath to accept only canonical
/sessions/<user>/<user>_<org>_<workspace>_<sessionId>.jsonl paths with a valid
UUID session ID, returning null for all other paths before cache lookup. Add a
test covering a noncanonical path that ends with an existing session ID and
verify it falls back to database rows rather than local cached content.

---

Nitpick comments:
In `@tests/claude-code/deeplake-fs.test.ts`:
- Around line 879-887: Update the mock query handler in the affected test to
compare the complete expected bootstrap and session-read SQL strings, including
the correct table, path predicate, and ordering, instead of using the generic
`includes("SELECT message FROM")` check. Return the configured rows only for
those exact queries and reject unexpected SQL; assert specific paths and
messages where the test validates results.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 35bd2f8c-b445-46bd-b8bd-c91130a897dd

📥 Commits

Reviewing files that changed from the base of the PR and between 5928e1f and 693c719.

📒 Files selected for processing (2)
  • src/shell/deeplake-fs.ts
  • tests/claude-code/deeplake-fs.test.ts

Comment thread src/shell/deeplake-fs.ts
Comment on lines +96 to +108
// Recover the bare capture sessionId from a VFS session path so a whole-session
// read can hit the local event cache. Path shape (see buildSessionPath):
// /sessions/<user>/<user>_<org>_<ws>_<sessionId>.jsonl
// The sessionId is a UUID (no underscores), so it is the last `_`-delimited
// segment of the filename stem — recoverable even when user/org/ws contain
// underscores. A non-matching path yields a value that simply misses the cache
// (→ DB fallback), so this is best-effort and never load-bearing.
function sessionIdFromVfsPath(p: string): string | null {
const file = p.split("/").pop() ?? "";
if (!file.endsWith(".jsonl")) return null;
const sid = file.slice(0, -".jsonl".length).split("_").pop() ?? "";
return sid.length > 0 ? sid : null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject non-session paths before consulting the cache.

The helper claims non-matching paths fall back, but any .jsonl filename ending in _<existing-session-id> resolves to that ID. A noncanonical session-table row could therefore render another session’s local cached content instead of its DB rows. Validate the /sessions/<user>/..._<UUID>.jsonl shape and UUID before returning an ID; add a DB-fallback test for a noncanonical path.

Proposed fix
 function sessionIdFromVfsPath(p: string): string | null {
-  const file = p.split("/").pop() ?? "";
-  if (!file.endsWith(".jsonl")) return null;
-  const sid = file.slice(0, -".jsonl".length).split("_").pop() ?? "";
-  return sid.length > 0 ? sid : null;
+  const match = /^\/sessions\/[^/]+\/.+_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(p);
+  return match?.[1] ?? null;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Recover the bare capture sessionId from a VFS session path so a whole-session
// read can hit the local event cache. Path shape (see buildSessionPath):
// /sessions/<user>/<user>_<org>_<ws>_<sessionId>.jsonl
// The sessionId is a UUID (no underscores), so it is the last `_`-delimited
// segment of the filename stem — recoverable even when user/org/ws contain
// underscores. A non-matching path yields a value that simply misses the cache
// (→ DB fallback), so this is best-effort and never load-bearing.
function sessionIdFromVfsPath(p: string): string | null {
const file = p.split("/").pop() ?? "";
if (!file.endsWith(".jsonl")) return null;
const sid = file.slice(0, -".jsonl".length).split("_").pop() ?? "";
return sid.length > 0 ? sid : null;
}
// Recover the bare capture sessionId from a VFS session path so a whole-session
// read can hit the local event cache. Path shape (see buildSessionPath):
// /sessions/<user>/<user>_<org>_<ws>_<sessionId>.jsonl
// The sessionId is a UUID (no underscores), so it is the last `_`-delimited
// segment of the filename stem — recoverable even when user/org/ws contain
// underscores. A non-matching path yields a value that simply misses the cache
// (→ DB fallback), so this is best-effort and never load-bearing.
function sessionIdFromVfsPath(p: string): string | null {
const match = /^\/sessions\/[^/]+\/.+_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(p);
return match?.[1] ?? null;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shell/deeplake-fs.ts` around lines 96 - 108, Update sessionIdFromVfsPath
to accept only canonical
/sessions/<user>/<user>_<org>_<workspace>_<sessionId>.jsonl paths with a valid
UUID session ID, returning null for all other paths before cache lookup. Add a
test covering a noncanonical path that ends with an existing session ID and
verify it falls back to database rows rather than local cached content.

@khustup2
khustup2 merged commit 5b07bcb into main Jul 24, 2026
12 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