Skip to content

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

@russellweiss

Description

@russellweiss

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.

Metadata

Metadata

Assignees

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