Skip to content

Parallelize MSBuild, Roslyn analysis, and Neo4j inserts across projects#15

Open
mscottford wants to merge 6 commits intovladbatushkov:mainfrom
mscottford:parallelize-build-and-analysis
Open

Parallelize MSBuild, Roslyn analysis, and Neo4j inserts across projects#15
mscottford wants to merge 6 commits intovladbatushkov:mainfrom
mscottford:parallelize-build-and-analysis

Conversation

@mscottford
Copy link
Copy Markdown
Contributor

Note: This PR depends on #13 (upgrade all dependencies to .NET 10) and #14 (fix projects dropped when added as transitive references).

Summary

  • Stage 1 — Build parallelism: Project builds in GetAnalysisContext now use .AsParallel(). Each p.Build() spawns an isolated MSBuild process, so there is no shared mutable state. AdhocWorkspace mutations remain sequential (Roslyn is not thread-safe for concurrent mutations).
  • Stage 3 — Analysis parallelism: The sequential per-project for-loop in Analyze is replaced with Task.WhenAll over Task.Run tasks, so CPU-bound syntax tree walking runs across thread-pool threads concurrently. The graph delete (--delete flag) is extracted to DbManager.DeleteData and called once upfront before the parallel loop to avoid races.
  • Stage 4 — Neo4j concurrency cap: A SemaphoreSlim(4) gates concurrent Neo4j sessions to avoid exhausting the connection pool on large solutions.
  • Logging: Added per-project progress output during the AddToWorkspace phase, which was previously a silent pause on large solutions.

Test plan

  • Run dotnet test — existing regression test should pass
  • Run the tool against a multi-project solution and confirm all projects are analyzed and inserted correctly
  • Confirm log output shows projects building and loading concurrently

🤖 Generated with Claude Code

mscottford and others added 6 commits April 10, 2026 13:57
- Targets net10.0 (up from net9.0)
- Buildalyzer/Workspaces 7.1.0 → 8.0.0
- Microsoft.CodeAnalysis/CSharp 4.13.0 → 5.3.0 (Analyzers pin removed)
- Neo4j.Driver 5.27.0 → 6.0.0
- System.CommandLine 2.0.0-beta4 → 2.0.5 (first stable release)
- Dockerfile base image updated to dotnet/sdk:10.0-alpine
- Neo4j image updated to 2026.03.1 with corrected env var names
- docker-compose.yml: removed obsolete version attribute, replaced
  hardcoded Windows volume path with relative ./SystemUnderTest path
- Code updated for breaking API changes in System.CommandLine 2.0
  (SetAction, Options.Add, Aliases.Add, Required, AllowMultipleArgumentsPerToken)
  and Neo4j.Driver 6.0 (IDriver.CloseAsync removed, use await using)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…from analysis

When AddToWorkspace(workspace, addProjectReferences: true) is called for a project,
Buildalyzer eagerly adds its referenced projects into the workspace. The previous
deduplication check silently dropped those projects on their own loop iteration,
causing them to receive no CONTAINS triple and no code analysis.

Adds a regression test using a two-project fixture solution where ProjectA references
ProjectB — ensuring both appear in the analysis context regardless of workspace
insertion order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stage 1: Build projects in parallel using .AsParallel() in GetAnalysisContext;
  each p.Build() spawns an isolated MSBuild process so there is no shared state.
  AdhocWorkspace mutations remain sequential (not thread-safe).

- Stage 3: Analyze and insert all projects concurrently using Task.WhenAll with
  Task.Run so CPU-bound syntax tree walking runs across thread-pool threads.
  The delete step is extracted to DbManager.DeleteData and run once upfront
  before the parallel loop to avoid races.

- Stage 4: A SemaphoreSlim(4) gates concurrent Neo4j sessions to avoid
  exhausting the connection pool on large solutions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The workspace population step (AddToWorkspace) is the slowest sequential
phase on large solutions and previously emitted no output, causing a
silent pause between "Building projects - finished." and "done."

Co-Authored-By: Claude Sonnet 4.6 <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