-
Notifications
You must be signed in to change notification settings - Fork 597
Description
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
- Create or use a Claude project with 500+ session files (~370MB of
.jsonlfiles) - Start Claude Code UI with
npm run dev - Access the UI through a browser
- Monitor CPU usage of the
node server/index.jsprocess
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)
- Reduce file watcher depth to 1 in `server/index.js:118`:
```javascript
depth: 1, // Only watch project directories, not individual files
```
-
Add TTL caching to `server/projects.js`:
- Cache `extractProjectDirectory()` results
- Cache `detectTaskMasterFolder()` results
- 30-second TTL is sufficient for UI responsiveness
-
Exclude session files from watcher in `server/index.js:103-114`:
```javascript
ignored: [
// ... existing ignores ...
'**/*.jsonl' // Don't watch session files
],
```
Medium-Term Improvements
- Add debouncing/throttling to `getProjects()` calls
- Implement incremental updates instead of full project scans
- Add memory limits for large session collections
- 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.