Skip to content

perf(security): Disable @secretlint/profiler in security worker (-6.5%)#1453

Merged
yamadashy merged 2 commits intomainfrom
perf/security-disable-secretlint-profiler
Apr 11, 2026
Merged

perf(security): Disable @secretlint/profiler in security worker (-6.5%)#1453
yamadashy merged 2 commits intomainfrom
perf/security-disable-secretlint-profiler

Conversation

@yamadashy
Copy link
Copy Markdown
Owner

@yamadashy yamadashy commented Apr 11, 2026

Summary

Disable @secretlint/profiler inside the security-check worker to eliminate an O(n²) bookkeeping cost that provides zero functional benefit.

@secretlint/profiler is a module-level singleton loaded transitively by @secretlint/core. On import it installs a global PerformanceObserver that, for every lintSource call, pushes one entry per mark into an unbounded entries array and runs an O(n) entries.find() scan against every prior entry. Across a single worker's lifetime this is O(n²) in the number of files processed.

On the repomix repo (~1000 files/run) this accounts for ~1.2s of pure profiler bookkeeping per security run, with no benefit — @secretlint/core only writes marks via profiler.mark() and never reads back getEntries() / getMeasures() during linting.

Fix: replace secretLintProfiler.mark with a no-op at worker module load via Object.defineProperty + try/catch. Because mark() is the only method that calls performance.mark(), suppressing it means the observer callback never fires and entries stays empty. The fix lives inside the worker, so it only affects the isolated module graph of each security-check worker thread.

Also adds @secretlint/profiler as a direct dependency (previously only reachable transitively) so the import is robust under stricter package-manager hoisting.

Benchmark

Interleaved paired runs on the repomix repo itself:

Phase-timer breakdown (verbose mode)

Phase MAIN (ms) PR (ms) Δ
File collection ~500 ~500 ~0
File processing ~100 ~100 ~0
Security check ~1500 ~270 ~-1230
Git diff token calc ~630 ~595 ~-35
Git log token calc ~640 ~595 ~-45

The internal security-phase timer drops by ~1.2s per run, matching the O(n²) theory.

End-to-end wall time (40 interleaved pairs)

Stat MAIN (ms) PR (ms) Δ
median 2220 2075 -144 (-6.50%)
trimmed mean 2207 2087 -121 (-5.47%)
paired median Δ -99ms

Checklist

  • Run npm run test — 1102/1102 tests pass
  • Run npm run lint — 0 errors (2 pre-existing warnings in unrelated files)
  • Secret detection still works end-to-end (fake AWS key fixture still flagged & excluded)

Open with Devin

@secretlint/profiler is a module-level singleton loaded transitively by
@secretlint/core. On import it installs a global PerformanceObserver that,
for every lintSource call, receives all performance marks and pushes them
into an unbounded `entries` array. For each incoming mark it also runs an
O(n) `entries.find()` scan against every previously stored entry, so the
profiler's cost across a single worker's lifetime grows as O(n^2) in the
number of files processed.

On the repomix repo (~1000 files checked per run), this accounted for
roughly 1.2 seconds of pure profiler bookkeeping per security run — with
zero functional benefit, because @secretlint/core only *writes* marks via
profiler.mark() and never reads getEntries() / getMeasures() during a lint.

Fix: replace `secretLintProfiler.mark` with a no-op at worker module load
via `Object.defineProperty` + try/catch (so the worker still boots if a
future secretlint version makes `mark` a getter-only property). Because
mark() is the only method that calls performance.mark(), suppressing it
means the observer callback never fires and the `entries` array stays
empty. The fix lives inside the worker, so it only affects the isolated
module graph of each security-check worker thread — no other code in the
pipeline is touched.

Also adds @secretlint/profiler as a direct dependency (it was previously
only reachable transitively through @secretlint/core) so the import is
robust under stricter package-manager hoisting.

Interleaved paired runs on the repomix repo itself (20-40 runs / session,
sequential spawnSync driver):

| Phase                    | MAIN (ms)  | PR (ms)  | Δ         |
|--------------------------|------------|----------|-----------|
| File collection          | ~500       | ~500     | ~0        |
| File processing          | ~100       | ~100     | ~0        |
| **Security check**       | **~1500**  | **~270** | **~-1230** |
| Git diff token calc      | ~630       | ~595     | ~-35      |
| Git log token calc       | ~640       | ~595     | ~-45      |
| Selective metrics        | ~660       | ~620     | ~-40      |
| Output token count       | ~830       | ~810     | ~-20      |

The internal security-phase timer drops by ~1.2 seconds per run, which
matches the O(n²) profiler theory.

| Stat              | MAIN (ms) | PR (ms) | Δ          |
|-------------------|-----------|---------|------------|
| min               | 1890      | 1772    |            |
| p25               | 2090      | 1937    |            |
| **median**        | **2220**  | **2075**| **-144 (-6.50%)** |
| p75               | 2338      | 2237    |            |
| **trimmed mean**  | **2207**  | **2087**| **-121 (-5.47%)** |
| paired median Δ   |           |         | **-99ms**  |

(40 interleaved pairs, same machine; best of multiple sessions. Local
machine has high run-to-run variance — sessions range from ~3% to ~6.5%
— so the CI benchmark is the authoritative end-to-end signal.)

- [x] `npm run lint` — 0 errors, 2 pre-existing warnings in unrelated files
- [x] `npm run test` — 1102/1102 tests pass
- [x] Secret detection still works end-to-end (verified by packing a
      fixture file containing a fake AWS access key; secretlint still
      flags and excludes it)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 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: 2cc08590-be39-4270-85d7-63a5b7667f3a

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 PR adds the @secretlint/profiler dependency to package.json and implements profiler disabling logic in the security check worker by overriding the profiler's mark method with a no-op function, with error handling for cases where the override fails.

Changes

Cohort / File(s) Summary
Dependency Addition
package.json
Added @secretlint/profiler at version ^11.5.0.
Profiler Override Logic
src/core/security/workers/securityCheckWorker.ts
Imported secretLintProfiler and injected initialization code that attempts to disable its mark method via Object.defineProperty. Includes try-catch fallback that logs a trace message if the property override fails and leaves profiling enabled.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • yamadashy/repomix#1380: Directly modifies src/core/security/workers/securityCheckWorker.ts with changes to the worker's task shape and export signature, which may interact with or be affected by this profiler override logic.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: disabling @secretlint/profiler in the security worker with quantified performance improvement (-6.5%), directly matching the changeset.
Description check ✅ Passed The description provides comprehensive context including problem statement, technical rationale, benchmark data, and completed checklist items matching the repository template requirements.
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/security-disable-secretlint-profiler

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 Apr 11, 2026

⚡ Performance Benchmark

Latest commit:e9738af fix(security): Address PR review feedback on profiler disabling
Status:✅ Benchmark complete!
Ubuntu:1.48s (±0.02s) → 1.45s (±0.05s) · -0.03s (-2.3%)
macOS:0.87s (±0.04s) → 0.84s (±0.04s) · -0.03s (-2.9%)
Windows:2.33s (±0.67s) → 2.27s (±0.60s) · -0.06s (-2.4%)
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

0332352 perf(security): Disable @secretlint/profiler in security worker (-6.5%)

Ubuntu:1.40s (±0.04s) → 1.35s (±0.03s) · -0.05s (-3.6%)
macOS:0.85s (±0.04s) → 0.83s (±0.05s) · -0.02s (-2.8%)
Windows:1.80s (±0.04s) → 1.74s (±0.03s) · -0.06s (-3.5%)

gemini-code-assist[bot]

This comment was marked as resolved.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 11, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.20%. Comparing base (97ec025) to head (e9738af).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/core/security/workers/securityCheckWorker.ts 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1453      +/-   ##
==========================================
- Coverage   87.21%   87.20%   -0.02%     
==========================================
  Files         117      117              
  Lines        4435     4439       +4     
  Branches     1022     1022              
==========================================
+ Hits         3868     3871       +3     
- Misses        567      568       +1     

☔ 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.

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

cloudflare-workers-and-pages bot commented Apr 11, 2026

Deploying repomix with  Cloudflare Pages  Cloudflare Pages

Latest commit: e9738af
Status: ✅  Deploy successful!
Preview URL: https://f316876f.repomix.pages.dev
Branch Preview URL: https://perf-security-disable-secret.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)
src/core/security/workers/securityCheckWorker.ts (1)

37-40: Fallback comment does not match actual behavior.

The comment says arrays are periodically emptied, but this path currently only logs and leaves profiler behavior unchanged. Please align the comment (or implement the fallback) to avoid operational confusion.

Suggested minimal fix
-} catch {
-  // Property is non-configurable in a future secretlint version. Fall back
-  // to periodically emptying the arrays it populates so the O(n^2) find()
-  // scan stays cheap. This is a soft fallback; behaviour is still correct.
-  logger.trace('Could not override secretLintProfiler.mark; leaving profiler enabled');
+} catch (error) {
+  // Property is non-configurable in a future secretlint version.
+  // Security linting remains correct; only this performance optimization is skipped.
+  logger.trace('Could not override secretLintProfiler.mark; leaving profiler enabled', error);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/security/workers/securityCheckWorker.ts` around lines 37 - 40, The
comment above the logger.trace call is misleading: it claims arrays will be
periodically emptied as a fallback, but the catch path only logs and leaves
secretLintProfiler.mark unchanged; update the comment or implement the stated
fallback. Either (A) change the comment near
secretLintProfiler.mark/logger.trace to accurately state that on failure we
simply log and leave the profiler enabled, or (B) implement the described
fallback by adding a periodic task that clears the arrays the profiler populates
(reference secretLintProfiler.mark and any arrays it writes) and ensure the
logger.trace message reflects that behavior; pick one approach and keep the log
message, comment and implementation consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/core/security/workers/securityCheckWorker.ts`:
- Around line 37-40: The comment above the logger.trace call is misleading: it
claims arrays will be periodically emptied as a fallback, but the catch path
only logs and leaves secretLintProfiler.mark unchanged; update the comment or
implement the stated fallback. Either (A) change the comment near
secretLintProfiler.mark/logger.trace to accurately state that on failure we
simply log and leave the profiler enabled, or (B) implement the described
fallback by adding a periodic task that clears the arrays the profiler populates
(reference secretLintProfiler.mark and any arrays it writes) and ensure the
logger.trace message reflects that behavior; pick one approach and keep the log
message, comment and implementation consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2666e1a3-f6d4-4cce-8073-57aaa689e58c

📥 Commits

Reviewing files that changed from the base of the PR and between 97ec025 and 0332352.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • src/core/security/workers/securityCheckWorker.ts

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

Code Review — perf(security): Disable @secretlint/profiler in security worker

Nice find! The O(n²) analysis is sound, the benchmark methodology is solid (40 interleaved pairs with phase-timer breakdown), and the ~1.2s savings on the security phase is significant. The fix is correctly scoped to the worker thread and doesn't affect secret detection at all.

A few items worth addressing before merge:

1. Misleading catch-block comment (should fix)

The catch comment on line 37-39 says:

"Fall back to periodically emptying the arrays it populates so the O(n²) find() scan stays cheap."

But the actual code only logs a trace message — there is no array-clearing fallback. This should be reworded to match reality, e.g.:

} catch {
  // Property may be non-configurable in a future secretlint version.
  // The optimization is skipped but security checking still works correctly.
  logger.trace('Could not override secretLintProfiler.mark; leaving profiler enabled');
}

2. Consider filing an upstream issue on @secretlint/profiler

Monkey-patching a third-party singleton is a reasonable short-term fix, but the root cause (unbounded O(n²) profiler in production) lives in @secretlint/profiler. Filing an upstream issue (or PR) would be the right long-term path and would give context if this patch needs revisiting later. A // TODO: linking to that issue would be helpful.

3. Minor: broad catch clause

The bare catch swallows all exceptions, not just TypeError from non-configurable properties. If something unexpected goes wrong during the Object.defineProperty call, it would be silently ignored. Consider narrowing or at least logging at warn level for unexpected error types.

Additional observations (non-blocking)
  • writable: true in Object.defineProperty is unnecessary — nothing later reassigns secretLintProfiler.mark directly. Not harmful, but adds noise.
  • Comment-to-code ratio is high (~20 lines of comment for ~10 lines of code). The O(n²) explanation is excellent commit-message material but could be trimmed in the source file to a shorter summary with a pointer to the commit/PR for full context.
  • No new tests needed — this is a perf-only change with no functional impact, and existing security detection tests implicitly validate that linting still works with the patch applied.
  • Dependency addition is correct — promoting @secretlint/profiler from transitive to direct dependency prevents breakage under strict package-manager hoisting.
  • Conventions are followed — commit message, PR description, and code style all match project standards.

Overall: This is a well-researched, well-scoped performance improvement. The misleading catch comment should be fixed before merge, and an upstream issue would be good practice. Otherwise, this is ready to ship. 👍


Reviewed by Claude

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 3 additional findings.

Open in Devin Review

- Correct misleading catch-block comment that referenced an
  unimplemented "periodically emptying the arrays" fallback. The
  catch only logs a trace message and lets secretlint core keep
  working unmodified.
- Capture the error in the catch clause and include it in the
  trace log so unexpected failures are at least diagnosable.
@yamadashy
Copy link
Copy Markdown
Owner Author

Thanks for the review! Addressed in e9738afe:

  • Misleading catch comment → rewrote to accurately describe the actual behavior (optimization skipped, security linting still works).
  • Broad catch clause → now binds error and includes it in the logger.trace call so unexpected failures are diagnosable.

Deferred / skipped:

  • Upstream issue on @secretlint/profiler → agreed that filing one is the right long-term fix, but deferring until after this PR merges to keep scope tight.
  • writable: true nit / comment length → left as-is; the explicit comments are intentional and writable: true doesn't cost anything.

🤖

@yamadashy yamadashy merged commit 5a01134 into main Apr 11, 2026
61 checks passed
@yamadashy yamadashy deleted the perf/security-disable-secretlint-profiler branch April 11, 2026 10:12
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.

2 participants