Skip to content

perf(security): Reduce security worker threads to minimize CPU contention#1369

Closed
yamadashy wants to merge 1 commit intomainfrom
perf/reduce-security-worker-threads
Closed

perf(security): Reduce security worker threads to minimize CPU contention#1369
yamadashy wants to merge 1 commit intomainfrom
perf/reduce-security-worker-threads

Conversation

@yamadashy
Copy link
Copy Markdown
Owner

@yamadashy yamadashy commented Mar 31, 2026

Summary

  • Add maxThreads option to WorkerOptions interface, allowing callers to explicitly cap worker thread count
  • Cap security check worker pool to 1 thread to eliminate CPU contention with the metrics worker pool

Background

Security check runs concurrently with file processing in the pipeline and completes well before metrics calculation begins. Previously, the security worker pool spawned threads based on task count (up to CPU core count), competing for CPU cores with the later metrics workers. On a 4-core machine, 4 security + 4 metrics threads caused ~140ms of CPU contention that inflated the metrics phase.

With 1 security thread, security still finishes before metrics while eliminating contention entirely.

Design

Rather than indirectly controlling thread count by capping numOfTasks (which depends on the internal TASKS_PER_THREAD constant), this PR adds an explicit maxThreads override to WorkerOptions. This makes the intent clear and is resilient to future changes in the thread calculation logic.

Checklist

  • Run npm run test
  • Run npm run lint

Open with Devin

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4da42007-2380-47aa-9417-a32becff9a3b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change introduces configurable worker thread limits for task runners. The WorkerOptions interface now accepts an optional maxThreads parameter, which is propagated through createWorkerPool and clamped to a minimum of 1. The security check module uses this feature to explicitly limit its worker pool to a single thread.

Changes

Cohort / File(s) Summary
Worker Pool Configuration
src/shared/processConcurrency.ts
Extended WorkerOptions to include optional maxThreads parameter; updated createWorkerPool to accept, validate (clamped to minimum 1), and apply this override when constructing Tinypool.
Security Check Integration
src/core/security/securityCheck.ts
Updated runSecurityCheck to pass maxThreads: 1 when initializing the security task runner, restricting the worker thread pool to a single thread.
Concurrency Tests
tests/shared/processConcurrency.test.ts
Added two test cases validating maxThreads override behavior: one asserting maxThreads: 1 is preserved, another confirming values below 1 are clamped to maxThreads: 1.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: reducing security worker threads to minimize CPU contention, which matches the core objective of this PR.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering summary, background, design rationale, and a completed checklist with test and lint confirmations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/reduce-security-worker-threads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

⚡ Performance Benchmark

Latest commit:1da63be perf(security): Reduce security worker threads to minimize CPU contention
Status:✅ Benchmark complete!
Ubuntu:1.93s (±0.05s) → 2.00s (±0.05s) · +0.07s (+3.4%)
macOS:1.01s (±0.17s) → 1.05s (±0.14s) · +0.03s (+3.2%)
Windows:2.47s (±0.51s) → 2.54s (±0.46s) · +0.07s (+2.9%)
Details
  • Packing the repomix repository with node bin/repomix.cjs
  • Warmup: 2 runs (discarded), interleaved execution
  • Measurement: 20 runs / 30 on macOS (median ± IQR)
  • Workflow run
History

1da8bc5 perf(security): Reduce security worker threads to minimize CPU contention

Ubuntu:1.82s (±0.01s) → 1.89s (±0.02s) · +0.07s (+3.9%)
macOS:1.45s (±0.20s) → 1.58s (±0.28s) · +0.13s (+8.9%)
Windows:2.38s (±0.37s) → 2.49s (±0.47s) · +0.11s (+4.5%)

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.96%. Comparing base (c8f24b7) to head (1da63be).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1369   +/-   ##
=======================================
  Coverage   86.96%   86.96%           
=======================================
  Files         116      116           
  Lines        4425     4426    +1     
  Branches     1025     1026    +1     
=======================================
+ Hits         3848     3849    +1     
  Misses        577      577           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a maxThreads override for worker pools, specifically capping the security check worker pool to one thread to minimize CPU contention. The changes include updates to the WorkerOptions interface, logic in createWorkerPool to handle the override with a minimum value of 1, and new unit tests. Feedback suggests adjusting the minThreads calculation to ensure it never exceeds the maxThreads override, preventing potential errors in the Tinypool library.

Comment on lines +67 to +68
const { minThreads, maxThreads: autoMaxThreads } = getWorkerThreadCount(numOfTasks);
const maxThreads = maxThreadsOverride != null ? Math.max(1, maxThreadsOverride) : autoMaxThreads;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When maxThreadsOverride is used to set a low thread count (e.g., 1), there is a risk that minThreads (returned by getWorkerThreadCount) could exceed it, which would cause Tinypool to throw an error (as it requires minThreads <= maxThreads). Although minThreads is currently hardcoded to 1 in getWorkerThreadCount, ensuring this relationship makes the code more robust against future changes in the thread calculation logic.

Suggested change
const { minThreads, maxThreads: autoMaxThreads } = getWorkerThreadCount(numOfTasks);
const maxThreads = maxThreadsOverride != null ? Math.max(1, maxThreadsOverride) : autoMaxThreads;
const { minThreads: autoMinThreads, maxThreads: autoMaxThreads } = getWorkerThreadCount(numOfTasks);
const maxThreads = maxThreadsOverride != null ? Math.max(1, maxThreadsOverride) : autoMaxThreads;
const minThreads = Math.min(autoMinThreads, maxThreads);

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 31, 2026

Deploying repomix with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1da63be
Status: ✅  Deploy successful!
Preview URL: https://67429cb4.repomix.pages.dev
Branch Preview URL: https://perf-reduce-security-worker.repomix.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/shared/processConcurrency.test.ts (1)

133-152: Good test coverage for the new maxThreads option.

The tests correctly verify:

  1. Override is respected when maxThreads: 1 is provided
  2. Values below 1 are clamped (tested with maxThreads: 0)

Consider adding a test for negative values (e.g., maxThreads: -1) to ensure the clamping logic handles all edge cases, though Math.max(1, ...) should handle this correctly.

🧪 Optional: Add test for negative maxThreads
+    it('should clamp negative maxThreads override to minimum of 1', () => {
+      createWorkerPool({ numOfTasks: 500, workerType: 'securityCheck', runtime: 'worker_threads', maxThreads: -5 });
+
+      expect(Tinypool).toHaveBeenCalledWith(
+        expect.objectContaining({
+          maxThreads: 1,
+        }),
+      );
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/shared/processConcurrency.test.ts` around lines 133 - 152, Add a test
case in the same suite that passes createWorkerPool({ numOfTasks: 500,
workerType: 'securityCheck', runtime: 'worker_threads', maxThreads: -1 }) and
assert that Tinypool was called with expect.objectContaining({ maxThreads: 1 })
to verify negative values are clamped; place it alongside the existing "should
clamp maxThreads override to minimum of 1" test and reference the
createWorkerPool helper and Tinypool mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/shared/processConcurrency.test.ts`:
- Around line 133-152: Add a test case in the same suite that passes
createWorkerPool({ numOfTasks: 500, workerType: 'securityCheck', runtime:
'worker_threads', maxThreads: -1 }) and assert that Tinypool was called with
expect.objectContaining({ maxThreads: 1 }) to verify negative values are
clamped; place it alongside the existing "should clamp maxThreads override to
minimum of 1" test and reference the createWorkerPool helper and Tinypool mock.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d6ded7c0-7c5b-4758-9b30-4b53d3a32a5c

📥 Commits

Reviewing files that changed from the base of the PR and between 47d77f9 and 1da8bc5.

📒 Files selected for processing (3)
  • src/core/security/securityCheck.ts
  • src/shared/processConcurrency.ts
  • tests/shared/processConcurrency.test.ts

@yamadashy
Copy link
Copy Markdown
Owner Author

Closing: benchmark shows +71ms regression on main. The original optimization (ff96545) was measured on a branch where security overlapped with metrics workers, causing CPU contention. On main, security and metrics run sequentially, so reducing security threads just slows the security phase with no contention benefit.

@yamadashy yamadashy closed this Mar 31, 2026
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 31, 2026

PR Review

Clean, well-scoped PR with good test coverage. A few observations:

1. Pipeline description is inaccurate

The PR description states "Security check runs concurrently with file processing" — but looking at src/core/packager.ts, security check (validateFileSafety, line 124) runs sequentially before file processing (processFiles, line 131). They are not concurrent.

The actual concurrency is between the security worker pool and the metrics warmup (pre-initialized at line 97-98), which is a single WASM-loading task on one thread. The contention story may still hold, but the description should be corrected for accuracy.

2. Defensive minThreads clamping

When maxThreads is overridden, minThreads is not clamped. Currently safe because getWorkerThreadCount always returns minThreads: 1, but if that logic ever changes, passing minThreads > maxThreads to Tinypool would be problematic.

Suggested fix
const maxThreads = maxThreadsOverride != null ? Math.max(1, maxThreadsOverride) : autoMaxThreads;
const finalMinThreads = Math.min(minThreads, maxThreads);

Then pass finalMinThreads to the Tinypool constructor.

3. Large repo consideration

Hardcoding maxThreads: 1 serializes all security checks. For repos with thousands of files, this could make the security phase the bottleneck instead of metrics. Were benchmarks run on large repos (10k+ files)? If the security check is I/O-bound and typically fast, this is likely fine — but worth confirming.

4. Minor test gap

The two new tests in processConcurrency.test.ts cover the createWorkerPool mechanism well. Consider also adding a test in the security check test suite asserting that initTaskRunner is called with maxThreads: 1 — this verifies the feature intent, not just the mechanism.


Overall: the implementation is clean, the maxThreads option is a well-designed extension point, and the code changes are minimal. The main action items are correcting the PR description and considering the defensive minThreads clamp.

🤖 Generated with Claude Code

@yamadashy yamadashy reopened this Mar 31, 2026
…tion

Add maxThreads option to WorkerOptions for explicit thread count control,
and cap security check worker pool to 1 thread.

Security runs concurrently with file processing and completes well before
the metrics calculation phase. On a 4-core machine, the previous config
spawned up to 4 security threads that competed for CPU cores with the
metrics workers, inflating the metrics phase by ~140ms. With 1 thread,
security still finishes before metrics while eliminating contention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@yamadashy yamadashy force-pushed the perf/reduce-security-worker-threads branch from 1da8bc5 to 1da63be Compare March 31, 2026 14:38
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 31, 2026

PR Review (Updated)

This PR is being closed per @yamadashy's comment — benchmark showed +71ms regression. Here's a final summary for the record.

Why the approach didn't work

The PR description assumed security and metrics workers compete for CPU cores. However, looking at src/core/packager.ts:129, security runs concurrently with file processing (which is main-thread work in the default config), not with metrics workers. Metrics calculation happens later in the pipeline. So capping security to 1 thread just serializes the security phase without reducing any real contention.

Code quality assessment

The implementation itself was clean and well-designed:

  • maxThreads option on WorkerOptions — good API extension, explicit intent
  • Math.max(1, maxThreadsOverride) — proper input clamping
  • Tests — covered the override and the clamping case
  • 100% coverage maintained per Codecov

One latent issue worth noting

If maxThreads override is ever reused: minThreads from getWorkerThreadCount() is not clamped against maxThreads. Currently safe since minThreads is always 1, but if auto-calculated minThreads ever exceeds the override, Tinypool would receive minThreads > maxThreads. Worth a defensive clamp if this option is reintroduced.


No further action needed given the closure.

🤖 Generated with Claude Code

@yamadashy
Copy link
Copy Markdown
Owner Author

Closing: the optimization is specific to CPU-constrained environments (4 cores). On machines with 8+ cores, security and metrics workers run without contention, and reducing security threads purely slows the security phase (+54ms on a high-core-count machine). Not a universal improvement.

@yamadashy yamadashy closed this Mar 31, 2026
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