From 47fb9e574b5eb52f3b28f6ff8646a58ab61fe939 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 03:44:45 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Prevent=20serialisation=20b?= =?UTF-8?q?ottleneck=20by=20converting=20Scanner=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/Scanner/CacheScanner.swift | 9 ++++++++- Sources/Cacheout/Scanner/NodeModulesScanner.swift | 8 +++++++- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..b58b7de --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-18 - Actor Serialization in TaskGroup +**Learning:** Using an `actor` to manage a `withTaskGroup` where child tasks invoke synchronous, blocking I/O (like `FileManager` operations) directly on the actor inadvertently serializes the tasks on the actor's context, preventing true parallelism and drastically reducing throughput. +**Action:** Change the `actor` to a `struct` for stateless components interacting with thread-safe dependencies (like `FileManager.default`) to allow tasks to execute concurrently across multiple threads. diff --git a/Sources/Cacheout/Scanner/CacheScanner.swift b/Sources/Cacheout/Scanner/CacheScanner.swift index 3ce3e9c..857cb41 100644 --- a/Sources/Cacheout/Scanner/CacheScanner.swift +++ b/Sources/Cacheout/Scanner/CacheScanner.swift @@ -26,7 +26,14 @@ import Foundation -actor CacheScanner { +// ⚡ Bolt Optimization: +// Changed from `actor` to `struct` because this component is stateless and only +// interacts with thread-safe dependencies (FileManager.default). +// Using an actor with `withTaskGroup` where child tasks invoke methods on `self` +// causes the tasks to serialize on the actor's context, destroying parallelism. +// As a struct, the child tasks can now execute concurrently across threads, +// significantly improving scan throughput for large file systems. +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..09003bc 100644 --- a/Sources/Cacheout/Scanner/NodeModulesScanner.swift +++ b/Sources/Cacheout/Scanner/NodeModulesScanner.swift @@ -28,7 +28,13 @@ import Foundation -actor NodeModulesScanner { +// ⚡ Bolt Optimization: +// Changed from `actor` to `struct` to prevent task serialization. This component +// is stateless (only holds thread-safe FileManager.default). In an actor, child +// tasks in `withTaskGroup` calling methods on `self` are forced to run sequentially. +// As a struct, the recursive filesystem traversals can run truly concurrently +// across multiple threads, drastically reducing scan time for deep hierarchies. +struct NodeModulesScanner { private let fileManager = FileManager.default /// Common directories where developers keep projects