From 6f04c28658a11c003c13b5a51e24650cfe280e75 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 04:22:39 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Fix=20TaskGroup=20serializa?= =?UTF-8?q?tion=20bottleneck=20by=20changing=20actors=20to=20structs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: acebytes <2820910+acebytes@users.noreply.github.com> --- .jules/bolt.md | 3 +++ Sources/Cacheout/Cleaner/CacheCleaner.swift | 3 ++- Sources/Cacheout/Scanner/CacheScanner.swift | 3 ++- Sources/Cacheout/Scanner/NodeModulesScanner.swift | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..69d7d97 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-04-10 - Actor TaskGroup Serialization Bottleneck +**Learning:** Using an `actor` to manage a `withTaskGroup` where tasks invoke synchronous, blocking I/O (like `FileManager` operations) directly on the actor inadvertently serializes the tasks, preventing parallelism. +**Action:** For stateless components interacting with thread-safe dependencies (like `FileManager`), use `struct`s or `nonisolated` methods to allow tasks to execute concurrently across threads. diff --git a/Sources/Cacheout/Cleaner/CacheCleaner.swift b/Sources/Cacheout/Cleaner/CacheCleaner.swift index 1d0e272..e1d346f 100644 --- a/Sources/Cacheout/Cleaner/CacheCleaner.swift +++ b/Sources/Cacheout/Cleaner/CacheCleaner.swift @@ -35,7 +35,8 @@ import Foundation import AppKit -actor CacheCleaner { +// Performance: Uses struct instead of actor as it has no mutable state, enabling true concurrency. +struct CacheCleaner { private let fileManager = FileManager.default func clean(results: [ScanResult], nodeModules: [NodeModulesItem] = [], moveToTrash: Bool) async -> CleanupReport { diff --git a/Sources/Cacheout/Scanner/CacheScanner.swift b/Sources/Cacheout/Scanner/CacheScanner.swift index 3ce3e9c..1865954 100644 --- a/Sources/Cacheout/Scanner/CacheScanner.swift +++ b/Sources/Cacheout/Scanner/CacheScanner.swift @@ -26,7 +26,8 @@ import Foundation -actor CacheScanner { +// Performance: Uses struct instead of actor to prevent withTaskGroup tasks from being serialized, allowing true concurrent scanning. +struct CacheScanner { private let fileManager = FileManager.default func scanAll(_ categories: [CacheCategory]) async -> [ScanResult] { diff --git a/Sources/Cacheout/Scanner/NodeModulesScanner.swift b/Sources/Cacheout/Scanner/NodeModulesScanner.swift index 3ed4d8c..2251f40 100644 --- a/Sources/Cacheout/Scanner/NodeModulesScanner.swift +++ b/Sources/Cacheout/Scanner/NodeModulesScanner.swift @@ -28,7 +28,8 @@ import Foundation -actor NodeModulesScanner { +// Performance: Uses struct instead of actor to prevent withTaskGroup tasks from being serialized, allowing true concurrent scanning. +struct NodeModulesScanner { private let fileManager = FileManager.default /// Common directories where developers keep projects