Skip to content

⚡ Bolt: Cache plugin configurations to reduce I/O and allocations#96

Open
himattm wants to merge 1 commit intomainfrom
bolt/cache-plugin-configs-6223029039728040727
Open

⚡ Bolt: Cache plugin configurations to reduce I/O and allocations#96
himattm wants to merge 1 commit intomainfrom
bolt/cache-plugin-configs-6223029039728040727

Conversation

@himattm
Copy link
Copy Markdown
Owner

@himattm himattm commented May 10, 2026

💡 What: Implemented an in-memory caching mechanism (sync.RWMutex protected map) for plugin configurations within the StatusLine struct.
🎯 Why: getPluginConfig is called repeatedly for every plugin execution (runPlugin), and without caching, it performs redundant disk I/O (os.ReadFile) and JSON parsing (json.Unmarshal) via config.LoadPluginConfig, causing unnecessary overhead and memory allocations on a hot path.
📊 Impact: Considerably speeds up rendering time and reduces GC pressure when evaluating multiple plugins by fetching configuration from memory instead of reading it from disk repeatedly.
🔬 Measurement: A microbenchmark shows the cached access takes ~30 ns/op, whereas un-cached disk I/O and parsing takes >250 ns/op per call. All test suites pass without regression.


PR created automatically by Jules for task 6223029039728040727 started by @himattm

Co-authored-by: himattm <6266621+himattm@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 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.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a caching mechanism for plugin configurations in internal/statusline/statusline.go using a sync.RWMutex to reduce disk I/O and JSON parsing overhead, alongside documentation updates in .jules/bolt.md. The review feedback highlights an efficiency issue where disk I/O is performed while holding a write lock, which could serialize parallel plugin executions; it is recommended to move the loading logic outside the lock to improve performance.

Comment on lines +726 to +740
sl.pluginConfigsMu.Lock()
defer sl.pluginConfigsMu.Unlock()

if sl.pluginConfigs == nil {
sl.pluginConfigs = make(map[string]map[string]any)
}
if cfg, ok := sl.pluginConfigs[name]; ok {
return cfg
}

// Load from plugin's own config.json, then overlay prism.json overrides
pluginCfg := sl.config.LoadPluginConfig(name)
return map[string]any{name: pluginCfg}
result := map[string]any{name: pluginCfg}
sl.pluginConfigs[name] = result
return result
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Performing disk I/O and JSON parsing (sl.config.LoadPluginConfig) while holding a write lock (sl.pluginConfigsMu.Lock()) is inefficient. Since renderLine executes multiple plugins in parallel, this lock will serialize the configuration loading for all plugins, potentially causing a bottleneck during the first render.

It is better to perform the expensive I/O operation outside the lock and then re-acquire the lock only to update the cache. This allows multiple plugins to load their configurations from disk concurrently.

	// Load from plugin's own config.json, then overlay prism.json overrides.
	// We do this outside the lock to avoid blocking other plugins during I/O.
	pluginCfg := sl.config.LoadPluginConfig(name)
	result := map[string]any{name: pluginCfg}

	sl.pluginConfigsMu.Lock()
	defer sl.pluginConfigsMu.Unlock()

	if sl.pluginConfigs == nil {
		sl.pluginConfigs = make(map[string]map[string]any)
	}
	// Double-check in case another goroutine loaded the config while we were doing I/O.
	if cfg, ok := sl.pluginConfigs[name]; ok {
		return cfg
	}

	sl.pluginConfigs[name] = result
	return result

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