Skip to content

⚡ Bolt: Optimize session history loading with bounded concurrency - #51

Merged
iotserver24 merged 1 commit into
mainfrom
bolt/optimize-session-history-loading-5635981684457449265
Apr 18, 2026
Merged

⚡ Bolt: Optimize session history loading with bounded concurrency#51
iotserver24 merged 1 commit into
mainfrom
bolt/optimize-session-history-loading-5635981684457449265

Conversation

@iotserver24

Copy link
Copy Markdown
Owner

💡 What: Replaced sequential file reads (for...of) with batched concurrent reads using Promise.all (chunk size 20) across session-manager.ts, history-manager.ts, and plan-session.ts.
🎯 Why: The file-per-session storage architecture created a significant I/O bottleneck when loading list views (e.g., thousands of chat sessions or plan sessions), making the load time O(n). Sequential reading is unnecessarily slow for independent file operations.
📊 Impact: ~3-4x speedup in loading times for users with many historical sessions, without hitting OS-level EMFILE (too many open files) errors thanks to the bounded chunk size.
🔬 Measurement: Verified with a local benchmark reading 5,000 mock JSON session files (Sequential: ~3046ms vs Batched: ~809ms). Tests pass successfully and code passes pnpm run build. Critical learning logged to .jules/bolt.md.


PR created automatically by Jules for task 5635981684457449265 started by @iotserver24

Replaces sequential file reading with batched Promise.all (bounded concurrency)
when loading chat sessions, history summaries, and plan sessions. This addresses
an I/O bottleneck when loading list views with many sessions.

Performance Impact:
Provides a ~3-4x speedup over sequential reads while avoiding EMFILE errors.
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@xibe-review

xibe-review Bot commented Apr 18, 2026

Copy link
Copy Markdown

Hey @iotserver24! 👋

I'll go through the changes and help you out with an automated review! 🔍

Starting the review now...

@iotserver24
iotserver24 marked this pull request as ready for review April 18, 2026 17:05
Copilot AI review requested due to automatic review settings April 18, 2026 17:05
@iotserver24

Copy link
Copy Markdown
Owner Author

@copilot review the changes

Copilot AI commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

@copilot review the changes

Reviewed commit 9c29e74: bounded concurrency logic looks correct in all 3 files, preserves malformed-file handling, and still sorts deterministically after reads. I validated locally with pnpm run build and pnpm run test (pass), and checked CI run 24609557313 job logs (all jobs successful, no failed jobs). No code changes were needed.

Copilot AI 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.

Pull request overview

Optimizes loading of session/history lists by replacing sequential per-file reads with bounded concurrent reads (chunked Promise.all) to reduce I/O bottlenecks in file-per-session storage.

Changes:

  • Implement chunked concurrent JSON file reads (limit 20) in SessionManager.listSessions, HistoryManager.list, and PlanSessionManager.listRecent.
  • Filter to .json files up-front before reading/parsing.
  • Document the performance learning and bounded-concurrency guideline in .jules/bolt.md.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/core/session-manager.ts Batched concurrent reads for session metadata loading.
src/core/plan-session.ts Batched concurrent reads for recent plan sessions loading.
src/core/history-manager.ts Batched concurrent reads for conversation summaries loading.
.jules/bolt.md Notes the bounded concurrency approach and benchmark results.
Comments suppressed due to low confidence (1)

src/core/history-manager.ts:149

  • HistoryManager has tests for summarizeTitle, but the updated list() behavior (bounded concurrency + skipping corrupted files + sorting) isn’t covered. Please add tests that mock readdir/readFile to validate list() returns correctly sorted summaries and continues past corrupted/unreadable files.
  async list(): Promise<ConversationSummary[]> {
    await this.ensureDir();
    try {
      const files = await fs.readdir(this.projectDir);
      const summaries: ConversationSummary[] = [];
      const jsonFiles = files.filter((file) => file.endsWith('.json'));

      // Bounded concurrency to speed up reading many session files
      const CONCURRENCY_LIMIT = 20;
      for (let i = 0; i < jsonFiles.length; i += CONCURRENCY_LIMIT) {
        const chunk = jsonFiles.slice(i, i + CONCURRENCY_LIMIT);

        await Promise.all(
          chunk.map(async (file) => {
            try {
              const filePath = path.join(this.projectDir, file);
              const content = await fs.readFile(filePath, 'utf-8');
              const conv = JSON.parse(content) as SavedConversation;
              summaries.push({
                id: conv.id,
                title: conv.title,
                created: conv.created,
                updated: conv.updated,
                messageCount: conv.messages.length,
                model: conv.model,
              });
            } catch {
              // Skip corrupted files
            }
          })
        );
      }

      // Sort by updated time, newest first
      summaries.sort((a, b) => new Date(b.updated).getTime() - new Date(a.updated).getTime());

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +156 to +173
// Bounded concurrency (chunks of 20) to prevent EMFILE errors while speeding up IO
const CONCURRENCY_LIMIT = 20;
for (let i = 0; i < jsonFiles.length; i += CONCURRENCY_LIMIT) {
const chunk = jsonFiles.slice(i, i + CONCURRENCY_LIMIT);

await Promise.all(
chunk.map(async (file) => {
const fullPath = path.join(this.sessionsDir, file);
try {
const raw = await fs.readFile(fullPath, 'utf-8');
const data = JSON.parse(raw) as ChatSession;
const { messages: _messages, stats: _stats, ...meta } = data;
metas.push(meta);
} catch {
// Ignore malformed files
}
})
);

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

The bounded-concurrency chunking logic is duplicated here and also mutates metas from within concurrent tasks via metas.push(...). Consider extracting a shared helper (e.g., mapWithConcurrencyLimit / runWithConcurrency) and have each task return its meta, then append results after Promise.all to avoid shared mutable state and reduce duplication across session/history/plan loaders.

Copilot uses AI. Check for mistakes.
Comment thread src/core/plan-session.ts
Comment on lines +92 to +108
const jsonFiles = files.filter(f => f.endsWith('.json'));

// Bounded concurrency
const CONCURRENCY_LIMIT = 20;
for (let i = 0; i < jsonFiles.length; i += CONCURRENCY_LIMIT) {
const chunk = jsonFiles.slice(i, i + CONCURRENCY_LIMIT);

await Promise.all(
chunk.map(async (file) => {
try {
const raw = await fs.readFile(path.join(this.sessionDir, file), 'utf8');
sessions.push(JSON.parse(raw) as PlanSession);
} catch {
// Ignore malformed session files to avoid breaking the list.
}
})
);

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

This change introduces new bounded-concurrency behavior in listRecent but there’s no test coverage asserting it still (1) ignores malformed JSON files and (2) returns the correct sorted+limited results when multiple session files exist. Since this module already has tests, please add/extend tests to cover listRecent with mocked readdir/readFile inputs.

Copilot uses AI. Check for mistakes.
Comment on lines +122 to +145
// Bounded concurrency to speed up reading many session files
const CONCURRENCY_LIMIT = 20;
for (let i = 0; i < jsonFiles.length; i += CONCURRENCY_LIMIT) {
const chunk = jsonFiles.slice(i, i + CONCURRENCY_LIMIT);

await Promise.all(
chunk.map(async (file) => {
try {
const filePath = path.join(this.projectDir, file);
const content = await fs.readFile(filePath, 'utf-8');
const conv = JSON.parse(content) as SavedConversation;
summaries.push({
id: conv.id,
title: conv.title,
created: conv.created,
updated: conv.updated,
messageCount: conv.messages.length,
model: conv.model,
});
} catch {
// Skip corrupted files
}
})
);

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

The bounded-concurrency implementation is duplicated across several managers and relies on mutating summaries from inside concurrent tasks. Consider extracting a shared concurrency helper and returning per-file summaries from the async callback (then merging) to keep the code easier to reason about and avoid shared mutable state in async flows.

Copilot uses AI. Check for mistakes.
@iotserver24
iotserver24 merged commit fa318ca into main Apr 18, 2026
12 checks passed
@iotserver24
iotserver24 deleted the bolt/optimize-session-history-loading-5635981684457449265 branch April 24, 2026 15:56
iotserver24 added a commit that referenced this pull request May 9, 2026
…-loading-5635981684457449265

⚡ Bolt: Optimize session history loading with bounded concurrency
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.

3 participants