Skip to content

perf(scan): move bundle-size walk off the scan critical path - #23

Merged
bounceconnection merged 1 commit into
devfrom
feature/scan-performance
Aug 20, 2026
Merged

bounceconnection merged 1 commit into
devfrom
feature/scan-performance

Conversation

@bounceconnection

Copy link
Copy Markdown
Owner

Problem

A full scan was slow because BundleMetadataExtractor recursively walked every file inside every plugin bundle on every scan just to populate the Size column. Audio bundles ship large sample/preset/wavetable trees (this library is ~65 GB across 1,800+ files), so each scan meant hundreds of thousands of filesystem stat calls — the dominant cost. It ran on the critical path and recomputed from scratch each time, even for unchanged plugins.

Fix

  • BundleMetadataExtractor no longer walks bundle contents. The scan now reads only Info.plist + the Mach-O header. PluginMetadata.fileSize is now optional (nil during the scan).
  • New BundleSizeService actor resolves sizes lazily after the list is shown, cached per bundle modification date — a rescan only re-walks bundles that actually changed.
  • Plugin gains a sizeCacheMtime field; PluginReconciler preserves the stored size when the scan produces none.
  • AppState starts the background size pass right after reconcile (overlapping the update check), for both full and incremental scans.

Net effect: the plugin list appears as soon as plist+arch metadata is read; size fills in shortly after and is essentially free on subsequent scans.

Tests

  • New BundleSizeService suite: cache hit / miss / stale-mtime, recursion, nonexistent dir, multi-bundle.
  • Extractor test asserting size is not computed during the scan.
  • Reconciler tests: stored size preserved when metadata size is absent; applied when present.
  • Full suite: 323 tests pass locally (swift test, macOS, Xcode 26.6). Build clean; SwiftLint clean on changed files.

Context

First increment of a larger effort to match PluginHub / Plugoff. Category classifier + offload backends and the remaining UI (duplicate detection, catalog + insights, offloading, PluginHub-style update UX) are on separate branches and will follow as their own PRs.

🤖 Generated with Claude Code

Summing every file inside every plugin bundle during the scan was the
dominant cost of a full scan (audio bundles ship large sample/preset
trees). BundleMetadataExtractor no longer walks bundle contents; the
scan now reads only Info.plist + the Mach-O header.

Bundle size is resolved lazily by a new BundleSizeService actor after
the plugin list is shown, cached per bundle modification date so a
rescan only re-walks bundles that actually changed. Plugin gains a
sizeCacheMtime field; PluginReconciler preserves the stored size when
the scan produces none (fileSize is now optional on PluginMetadata).

Adds BundleSizeService tests (cache hit/miss/stale) and reconciler
coverage for size preservation. Full suite: 323 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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