Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 8 additions & 1 deletion Sources/Cacheout/Scanner/CacheScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand Down
8 changes: 7 additions & 1 deletion Sources/Cacheout/Scanner/NodeModulesScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down