claudecodeui icon indicating copy to clipboard operation
claudecodeui copied to clipboard

Severe Performance Degradation with Large Session File Counts - CPU Usage 130%+

Open russellweiss opened this issue 5 months ago • 1 comments

Environment

  • Version: 1.8.10
  • Node.js: v20.19.5
  • npm: 10.8.2
  • OS: Linux (Ubuntu/Debian)
  • Platform: x86_64, 24-core system

Description

The Claude Code UI backend server experiences severe performance issues (130%+ CPU usage, request timeouts) when a project has a large number of session files (~500+ .jsonl files in ~/.claude/projects/[project-name]/).

Steps to Reproduce

  1. Create or use a Claude project with 500+ session files (~370MB of .jsonl files)
  2. Start Claude Code UI with npm run dev
  3. Access the UI through a browser
  4. Monitor CPU usage of the node server/index.js process

Expected Behavior

  • CPU usage should remain reasonable (< 20%)
  • API requests should complete successfully
  • No timeout errors from reverse proxy/tunnel

Actual Behavior

  • CPU usage: 130%+ constantly (on a single core, maxing it out)
  • Request timeouts: Dozens of "context canceled" errors per minute
  • Memory usage: 3-4GB (excessive)
  • UI responsiveness: Severe delays or complete unresponsiveness

Root Causes Identified

1. File Watcher Depth Too High (server/index.js:118)

```javascript depth: 3, // Reduced from 10 to 3 for performance ```

  • Watches 638 individual session files instead of just directories
  • Every file change triggers expensive operations
  • Fix: Reduce `depth` to `1` (only watch project directories, not individual session files)

2. Missing Cache TTL (server/projects.js)

The following expensive operations have no Time-To-Live caching:

  • `extractProjectDirectory()` - Parses all `.jsonl` files to find project path
  • `detectTaskMasterFolder()` - Scans filesystem for TaskMaster files
  • `getSessions()` - Reads and parses session files

These functions are called on every API request without caching, causing:

  • Repeated parsing of 500+ files
  • Expensive filesystem operations
  • High CPU usage

Fix: Add 30-second TTL cache:

```javascript const CACHE_TTL = 30000; // 30 seconds const cache = new Map(); // key -> {data, timestamp}

function getCached(cache, key) { const cached = cache.get(key); if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) { return cached.data; } return null; }

function setCache(cache, key, data) { cache.set(key, { data, timestamp: Date.now() }); } ```

3. getProjects() Called Too Frequently

The `getProjects()` function in `server/projects.js:393` performs 4 expensive operations per project:

  • Extract project directory (parses all sessions)
  • Get 5 most recent sessions
  • Get Cursor sessions
  • Detect TaskMaster folder

This is called on every file watcher event, which with `depth: 3` means hundreds of times per minute.

Performance Impact

Before fixes:

  • CPU: 130%+ constant
  • Timeouts: Dozens per minute
  • Memory: 3-4GB
  • Status: Barely functional

After applying fixes:

  • CPU: 6-7% (95% reduction!)
  • Timeouts: 0
  • Memory: ~700MB
  • Status: Perfectly stable (tested over 6 hours)

Proposed Solutions

Immediate Fixes (Low Risk)

  1. Reduce file watcher depth to 1 in `server/index.js:118`:

```javascript depth: 1, // Only watch project directories, not individual files ```

  1. Add TTL caching to `server/projects.js`:

    • Cache `extractProjectDirectory()` results
    • Cache `detectTaskMasterFolder()` results
    • 30-second TTL is sufficient for UI responsiveness
  2. Exclude session files from watcher in `server/index.js:103-114`:

```javascript ignored: [ // ... existing ignores ... '**/*.jsonl' // Don't watch session files ], ```

Medium-Term Improvements

  1. Add debouncing/throttling to `getProjects()` calls
  2. Implement incremental updates instead of full project scans
  3. Add memory limits for large session collections
  4. Consider lazy-loading session data on-demand

Additional Context

  • Project structure matters: Having large projects (300MB+) inside the claudecodeui directory exacerbates the issue
  • The file watcher broadcasts updates to all connected WebSocket clients, multiplying the load
  • Session files don't need real-time monitoring - directory-level watching is sufficient

Files to Review

  • `server/index.js` (lines 92-179) - File watcher setup
  • `server/projects.js` (lines 282-391, 393-490) - Project discovery and caching
  • `server/projects.js` (lines 70-233) - TaskMaster detection

I've tested these fixes in production for 6+ hours with excellent results and can provide a PR if helpful.

russellweiss avatar Oct 02 '25 02:10 russellweiss

The problem with the sessions is yo need to watch them since the sidebar is dependant on them. Moreover all session IDs and messages are coming from them actually. I'd be happy to review a PR on that though @russellweiss

viper151 avatar Oct 31 '25 01:10 viper151