Skip to content

perf: reduce per-event allocations in streaming pipeline#93

Closed
Nomadcxx wants to merge 2 commits into
mainfrom
fix/streaming-pipeline-perf
Closed

perf: reduce per-event allocations in streaming pipeline#93
Nomadcxx wants to merge 2 commits into
mainfrom
fix/streaming-pipeline-perf

Conversation

@Nomadcxx

@Nomadcxx Nomadcxx commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • sdk-child.ts: Module-level TextEncoder singleton instead of new TextEncoder() on every streamed event. Eliminates object allocation churn on the hot path.
  • logger.ts: Cache CURSOR_ACP_LOG_LEVEL, CURSOR_ACP_LOG_SILENT, and CURSOR_ACP_LOG_CONSOLE at module load. Replaces per-call process.env reads in shouldLog() and isConsoleEnabled() with cached constants. _resetLoggerState() re-reads env for tests.
  • logger.test.ts: Tests that set env vars now call _resetLoggerState() after so cached values pick them up.

Relates to #92 — this is the first of two PRs targeting quick wins from the performance audit. These are internal-only changes with no API or behaviour impact.

What this does NOT change

  • No tracker swap (MixedDeltaTracker stays — git history shows it exists to handle mixed delta/accumulated streams and swapping it would reintroduce duplicate output)
  • No HTTP contract changes
  • No tool-loop or provider boundary changes

Test plan

  • Full test suite: 644 pass, 0 fail, 2 skip (pre-existing)
  • npm run build clean

Summary by CodeRabbit

  • Refactor

    • Optimized event message handling and logging configuration evaluation.
  • Tests

    • Enhanced logger configuration test coverage, including environment variable scenarios and state reset validation.

- Move TextEncoder to module-level singleton in sdk-child.ts instead of
  creating a new instance per streamed event
- Cache logger env-var reads (log level, silent, console) at module load
  instead of hitting process.env on every shouldLog/isConsoleEnabled call
- Update logger tests to call _resetLoggerState() after setting env vars
  so cached values are refreshed

Part of #92
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6041bd64-cd2a-4c2c-afaa-62d62075aabb

📥 Commits

Reviewing files that changed from the base of the PR and between 4f37c5b and dcb04b9.

📒 Files selected for processing (3)
  • src/client/sdk-child.ts
  • src/utils/logger.ts
  • tests/unit/utils/logger.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/logger.ts

📝 Walkthrough

Walkthrough

Two unrelated micro-optimizations: sdk-child.ts moves TextEncoder construction to a shared module-level instance instead of allocating one per event message, and logger.ts replaces per-call process.env reads with values cached at module load, updating _resetLoggerState() and test setup accordingly.

Changes

TextEncoder reuse in sdk-child

Layer / File(s) Summary
Shared TextEncoder for event forwarding
src/client/sdk-child.ts
Declares one module-level TextEncoder and updates the per-request event enqueue path to encode JSON event lines with that shared instance instead of constructing a new one each time.

Logger environment-variable caching

Layer / File(s) Summary
Module-level env caching and createLogger console gating
src/utils/logger.ts
Adds readConfiguredLevel() and module-load-time constants (CONFIGURED_LEVEL, IS_SILENT, CONSOLE_ENABLED, CONFIGURED_PRIORITY); shouldLog() and _resetLoggerState() use these cached values; debug/info/warn/error in createLogger switch from per-call isConsoleEnabled() to the cached CONSOLE_ENABLED flag.
Test setup and caching behavior verification
tests/unit/utils/logger.test.ts
Inserts _resetLoggerState() in beforeEach after clearing env vars and in CURSOR_ACP_LOG_CONSOLE=1 and CURSOR_ACP_LOG_SILENT=1 tests after mutating process.env to synchronize cached module state with test expectations; adds two new test cases verifying that CURSOR_ACP_LOG_LEVEL filters logs only after _resetLoggerState() is called, and that log-level changes are applied only after reset.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A TextEncoder shares its work,

No wasteful new each time,

And logger caches truth once cached,

No process.env climb,

Small steps to speed the flow! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: reducing per-event allocations in the streaming pipeline through TextEncoder reuse and environment variable caching.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/streaming-pipeline-perf

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unit/utils/logger.test.ts (1)

28-35: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move _resetLoggerState() call to after environment cleanup.

The current order calls _resetLoggerState() at line 30 before clearing environment variables (lines 32-34), which means the module-level cache is populated with potentially stale values and then the env vars are cleared, leaving the cached state inconsistent with the cleaned environment.

If the test environment has any of these variables set (e.g., CURSOR_ACP_LOG_CONSOLE=1), the cached CONSOLE_ENABLED would be true even after the env var is deleted, potentially causing tests like "writes logs to file by default (not console)" to fail.

🔧 Proposed fix
 beforeEach(() => {
   vi.clearAllMocks();
-  _resetLoggerState(); // Reset module-level state before each test
   process.env = { ...originalEnv };
   delete process.env.CURSOR_ACP_LOG_LEVEL;
   delete process.env.CURSOR_ACP_LOG_SILENT;
   delete process.env.CURSOR_ACP_LOG_CONSOLE;
+  _resetLoggerState(); // Reset module-level state AFTER clearing env vars
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/utils/logger.test.ts` around lines 28 - 35, The
_resetLoggerState() function is being called before the environment variables
are cleaned up in the beforeEach hook. Move the _resetLoggerState() call to
execute after all the process.env deletions (after the delete
process.env.CURSOR_ACP_LOG_CONSOLE line) so that the module-level cache is reset
based on the cleaned environment state rather than being populated with
potentially stale values before the environment is cleared.
🧹 Nitpick comments (1)
src/utils/logger.ts (1)

30-37: ⚡ Quick win

Consider extracting env-reading logic into helper functions.

The boolean logic for IS_SILENT (lines 31-33) and CONSOLE_ENABLED (lines 34-36) is duplicated in _resetLoggerState() (lines 69-74). Following the pattern of readConfiguredLevel(), consider extracting these into helper functions to eliminate duplication and ensure consistency.

♻️ Suggested refactor
+function readIsSilent(): boolean {
+  return (
+    process.env.CURSOR_ACP_LOG_SILENT === "1" ||
+    process.env.CURSOR_ACP_LOG_SILENT === "true"
+  );
+}
+
+function readConsoleEnabled(): boolean {
+  return (
+    process.env.CURSOR_ACP_LOG_CONSOLE === "1" ||
+    process.env.CURSOR_ACP_LOG_CONSOLE === "true"
+  );
+}
+
 let CONFIGURED_LEVEL: LogLevel = readConfiguredLevel();
-let IS_SILENT: boolean =
-  process.env.CURSOR_ACP_LOG_SILENT === "1" ||
-  process.env.CURSOR_ACP_LOG_SILENT === "true";
-let CONSOLE_ENABLED: boolean =
-  process.env.CURSOR_ACP_LOG_CONSOLE === "1" ||
-  process.env.CURSOR_ACP_LOG_CONSOLE === "true";
+let IS_SILENT: boolean = readIsSilent();
+let CONSOLE_ENABLED: boolean = readConsoleEnabled();
 let CONFIGURED_PRIORITY = LEVEL_PRIORITY[CONFIGURED_LEVEL];

Then update _resetLoggerState():

 export function _resetLoggerState(): void {
   logDirEnsured = false;
   logFileError = false;
   CONFIGURED_LEVEL = readConfiguredLevel();
-  IS_SILENT =
-    process.env.CURSOR_ACP_LOG_SILENT === "1" ||
-    process.env.CURSOR_ACP_LOG_SILENT === "true";
-  CONSOLE_ENABLED =
-    process.env.CURSOR_ACP_LOG_CONSOLE === "1" ||
-    process.env.CURSOR_ACP_LOG_CONSOLE === "true";
+  IS_SILENT = readIsSilent();
+  CONSOLE_ENABLED = readConsoleEnabled();
   CONFIGURED_PRIORITY = LEVEL_PRIORITY[CONFIGURED_LEVEL];
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/logger.ts` around lines 30 - 37, The environment variable reading
logic for IS_SILENT and CONSOLE_ENABLED is duplicated between the module-level
initialization and the _resetLoggerState() function. Following the existing
pattern of readConfiguredLevel(), create two new helper functions (e.g.,
readIsSilent() and readConsoleEnabled()) that encapsulate the boolean logic for
checking CURSOR_ACP_LOG_SILENT and CURSOR_ACP_LOG_CONSOLE environment variables
respectively. Then replace the duplicated logic in both the initial variable
assignments and the _resetLoggerState() function with calls to these helper
functions to ensure consistency and eliminate code duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/unit/utils/logger.test.ts`:
- Around line 28-35: The _resetLoggerState() function is being called before the
environment variables are cleaned up in the beforeEach hook. Move the
_resetLoggerState() call to execute after all the process.env deletions (after
the delete process.env.CURSOR_ACP_LOG_CONSOLE line) so that the module-level
cache is reset based on the cleaned environment state rather than being
populated with potentially stale values before the environment is cleared.

---

Nitpick comments:
In `@src/utils/logger.ts`:
- Around line 30-37: The environment variable reading logic for IS_SILENT and
CONSOLE_ENABLED is duplicated between the module-level initialization and the
_resetLoggerState() function. Following the existing pattern of
readConfiguredLevel(), create two new helper functions (e.g., readIsSilent() and
readConsoleEnabled()) that encapsulate the boolean logic for checking
CURSOR_ACP_LOG_SILENT and CURSOR_ACP_LOG_CONSOLE environment variables
respectively. Then replace the duplicated logic in both the initial variable
assignments and the _resetLoggerState() function with calls to these helper
functions to ensure consistency and eliminate code duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b34bb98c-29b2-4f8a-a6e8-f14cefbb925f

📥 Commits

Reviewing files that changed from the base of the PR and between 9b0a77d and 4f37c5b.

📒 Files selected for processing (3)
  • src/client/sdk-child.ts
  • src/utils/logger.ts
  • tests/unit/utils/logger.test.ts

Run _resetLoggerState after clearing log-related env vars so cached logger settings match the test harness instead of the parent shell.

Add coverage for log-level env caching and refresh.
@Nomadcxx

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Nomadcxx Nomadcxx force-pushed the fix/streaming-pipeline-perf branch from 4b730db to dcb04b9 Compare June 16, 2026 12:01
@Nomadcxx

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Nomadcxx

Copy link
Copy Markdown
Owner Author

Superseded by #94 and #95 which include all changes from this PR plus additional improvements.

@Nomadcxx Nomadcxx closed this Jun 16, 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