Skip to content

agents: add runtime control for asset collection#377

Closed
santigimeno wants to merge 1 commit intonode-v24.x-nsolid-v6.xfrom
santi/assets_enabled
Closed

agents: add runtime control for asset collection#377
santigimeno wants to merge 1 commit intonode-v24.x-nsolid-v6.xfrom
santi/assets_enabled

Conversation

@santigimeno
Copy link
Copy Markdown
Member

@santigimeno santigimeno commented Oct 10, 2025

Add comprehensive runtime control for asset collection (CPU/heap profiling, snapshots) with multiple configuration methods.

Implements assetsEnabled flag with full control surface:

  • NSOLID_ASSETS_ENABLED environment variable
  • nsolid.start({ assetsEnabled: true/false }) configuration
  • Runtime helpers: enableAssets() and disableAssets()
  • Dynamic updates via gRPC reconfigure protocol
  • Validation in both gRPC and ZMQ agents with proper errors
  • Continuous profiler integration respects assetsEnabled state

This allows operators to disable expensive profiling operations at runtime without process restarts, improving resource management and production safety.

Also adds missing enableTraces()/disableTraces() helper methods to complete the tracing API surface.

Comprehensive test coverage added for all asset types across gRPC, ZMQ, and parallel test suites.

Summary by CodeRabbit

  • New Features

    • Public runtime toggles and getters for assets and tracing (enableAssets/disableAssets, enableTraces/disableTraces, assetsEnabled, tracingEnabled); config supports assetsEnabled and continuous CPU profiling gating.
  • Behavior

    • Asset-related operations (heap/profile/sampling/snapshot/continuous profiling) and tracing now honor assetsEnabled/tracingEnabled and return a clear error when assets collection is disabled.
  • Tests

    • Expanded test coverage validating enable/disable flows across GRPC/ZMQ agents, streams, sampling, snapshots, profiling and worker/thread scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@santigimeno santigimeno self-assigned this Oct 10, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 10, 2025

Walkthrough

Added an optional proto field assetsEnabled; agents (gRPC/ZMQ) gain an atomic assets flag and gate asset-related operations (returning EAssetsDisabled when off); JS exposes enable/disable APIs and normalizes config; many tests added to validate assets/tracing toggles and behavior.

Changes

Cohort / File(s) Summary
Protocol Buffers
agents/grpc/proto/reconfigure.proto, agents/grpc/src/proto/reconfigure.pb.h, agents/grpc/src/proto/reconfigure.pb.cc
Added optional bool assetsEnabled = 13 to ReconfigureBody; regenerated headers/cc updated accessors, has-bits, parse tables, serialization, layout, and migration schema.
gRPC Agent
agents/grpc/src/grpc_agent.h, agents/grpc/src/grpc_agent.cc
Added assets_enabled_ and cont_cpu_profile_enabled_ (atomic bools); read assetsEnabled from config/reconfigure; include flag in reconfigure payloads; gate profiling start and return EAssetsDisabled when assets disabled; enforce continuous CPU profiling gating.
ZMQ Agent
agents/zmq/src/zmq_agent.h, agents/zmq/src/zmq_agent.cc
Added assets_enabled_ (atomic bool, default true); read assetsEnabled from config; short-circuit profiling initiation when assets disabled (error/EINVAL).
Errors
agents/grpc/src/grpc_errors.h
Added EAssetsDisabled to GRPC_ERRORS macro / ErrorType enum (code 1008, message "Assets collection disabled").
Core / Native
src/nsolid/nsolid_api.cc
Gate reading/propagation of contCpuProfile and contCpuProfileInterval behind assetsEnabled; only apply continuous-profiler updates when assets enabled.
JavaScript API
lib/nsolid.js
Added public enableAssets(), disableAssets(), enableTraces(), disableTraces() and getters assetsEnabled, tracingEnabled; normalize config via optionToBool; heapProfile/heapSampling streams now validate assetsEnabled and throw when assets disabled.
Test Clients (gRPC / ZMQ)
test/common/nsolid-grpc-agent/*, test/common/nsolid-zmq-agent/*
Added IPC handlers and TestClient methods: config(), enableAssets(), disableAssets(), enableTraces(), disableTraces(), trace(); centralized send/wait helper #sendAndWait; guarded Otel registration in client.
gRPC Tests
test/agents/test-grpc-*.mjs
Added tests asserting 500 / EAssetsDisabled when assets disabled via env or runtime toggles; validate re-enable flows for continuous-profile, heap-profile, heap-sampling, profile, snapshot, and tracing; added phase-based tracing tests.
ZMQ Tests
test/agents/test-zmq-*.mjs
Added assets toggle tests asserting blocked start (422) when disabled and success after re-enable across heap/profile/sampling/snapshot; added tracing enable/disable tests.
Parallel Tests & Helpers
test/parallel/test-nsolid-*.js, test/common/*
Added helper routines (runAssetsToggleTests, runHeapProfileAssetsToggleTests, runHeapSamplingAssetsToggleTests, runSnapshotAssetsToggleTests); replaced some static timeouts with common.platformTimeout(100); integrated toggle flows.
Test modifications
multiple files under test/agents/ and test/parallel/
Numerous new test cases added; some test blocks appear duplicated in multiple insertion points (review for accidental duplicates).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant JS as JS TestClient / App
    participant IPC as Test Child (agent wrapper)
    participant Agent as Agent (gRPC / ZMQ)
    participant Profiler as Profiler subsystem

    JS->>IPC: send config / enableAssets / disableAssets / trace
    IPC->>Agent: translate -> reconfigure / control message
    Agent->>Agent: update atomic assets_enabled_ / cont_cpu_profile_enabled_
    alt assetsEnabled == false
        Agent-->>IPC: respond with EAssetsDisabled (mapped -> 500/422)
        IPC-->>JS: surface error to caller
    else assetsEnabled == true
        Agent->>Profiler: request start (profile/sampling/snapshot/trace)
        Profiler-->>Agent: emit asset-data events / completion
        Agent-->>IPC: forward events
        IPC-->>JS: deliver events/results
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect generated proto changes: has-bit indices, parse tables, serialization offsets.
  • Verify consistent mapping and propagation of assetsEnabled through reconfigure paths and reconfigure payloads.
  • Confirm EAssetsDisabled is surfaced consistently across gRPC/native/JS error mappings and tests.
  • Review atomic usage and thread-safety around assets_enabled_ and runtime toggles.
  • Audit tests for accidental duplicate insertions and ensure timeouts/async waits are correct.

Suggested reviewers

  • juanarbol
  • RafaelGSS

Poem

🐰 I nibble bytes and toggle lights,

When assets nap I hush their flights.
Flip me on, the profiles sing,
Flip me off, the silence rings.
A rabbit hops — and metrics spring.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding runtime control for asset collection. It accurately reflects the PR's core objective across multiple files and configuration surfaces.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch santi/assets_enabled

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

Copy link
Copy Markdown

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
agents/grpc/src/grpc_agent.cc (1)

1710-1760: Propagate assetsEnabled via reconfigure().

reconfigure() ignores the new proto field, so gRPC-driven config updates can’t flip the assets gate and the feature regresses. Please write the field into the outbound JSON before calling UpdateConfig().

   if (body.has_contcpuprofile()) {
       out["contCpuProfile"] = body.contcpuprofile();
   }
+
+  if (body.has_assetsenabled()) {
+      out["assetsEnabled"] = body.assetsenabled();
+  }
🧹 Nitpick comments (4)
test/parallel/test-nsolid-heap-sampling-stream.js (2)

191-224: Consider refactoring nested callbacks to improve readability.

The test uses four levels of nested setImmediate() callbacks, making the flow difficult to follow. Consider refactoring to async/await or helper functions to flatten the structure.

Example refactor using helper functions:

function runHeapSamplingStreamAssetsToggleTests() {
  // Disable assets through config update
  nsolid.start({
    command: 'localhost:9001',
    data: 'localhost:9002',
    assetsEnabled: false,
  });

  setImmediate(() => {
    testDisabled(() => {
      testEnabled(() => {
        testDisabled(() => {
          testFinalEnabled();
        });
      });
    });
  });
}

function testDisabled(callback) {
  assert.throws(
    () => nsolid.heapSamplingStream(0, 1000),
    { message: 'Heap sampling could not be started' }
  );
  callback();
}

function testEnabled(callback) {
  nsolid.enableAssets();
  setImmediate(() => {
    let profile = '';
    const stream = nsolid.heapSamplingStream(0, 1200);
    stream.on('data', (chunk) => { profile += chunk; });
    stream.on('end', common.mustCall(() => {
      assert(JSON.parse(profile));
      testProfileSchema(JSON.parse(profile));
      callback();
    }));
  });
}

function testFinalEnabled() {
  nsolid.enableAssets();
  setImmediate(() => {
    const stream = nsolid.heapSamplingStream(0, 1200);
    stream.resume();
    stream.on('end', common.mustCall(() => {
      assert.ok(true);
    }));
  });
}

193-199: Consider adding memory allocations for consistency.

The initial test (lines 128-131) performs allocations to ensure there's data to sample. The subsequent heap sampling calls don't do this, which might lead to less deterministic results. While the tests may still pass, adding allocations would make the behavior more consistent.

Add allocations before each heap sampling operation:

     nsolid.enableAssets();
     setImmediate(() => {
+      // Do some allocations to ensure sampling data
+      const arr = [];
+      for (let i = 0; i < 1000; i++) {
+        arr.push(new Array(1000));
+      }
+      
       let profile = '';
       const enabledStream = nsolid.heapSamplingStream(0, 1200);

Apply the same pattern before line 216.

Also applies to: 216-221

test/parallel/test-nsolid-profile.js (1)

120-178: Reduce reliance on fixed sleeps; gate on observed config state

The toggle flow is correct, but hardcoded 100ms delays can flake under slow CI or propagation lag. After each start()/enable/disable step, prefer waiting until nsolid.config.assetsEnabled reflects the expected value before proceeding (or assert it directly), instead of fixed timeouts.

Example pattern:

function waitForAssetsState(expected, deadline = Date.now() + 2000) {
  return new Promise((resolve, reject) => {
    (function poll() {
      if (nsolid.config.assetsEnabled === expected) return resolve();
      if (Date.now() > deadline) return reject(new Error('assetsEnabled not applied'));
      setImmediate(poll);
    })();
  });
}

// Usage:
nsolid.start({ /* … */ assetsEnabled: false });
await waitForAssetsState(false);
// then assert throws, etc.
test/parallel/test-nsolid-heap-profile.js (1)

133-200: Consider using async/await instead of nested setTimeout calls.

The nested setTimeout pattern (lines 141, 167, 173, 193) makes the test flow harder to follow and maintain. Consider refactoring to use async/await with timer promises:

-function runHeapProfileAssetsToggleTests() {
+async function runHeapProfileAssetsToggleTests() {
   // Disable assets via config update
   nsolid.start({
     command: 'localhost:9001',
     data: 'localhost:9002',
     assetsEnabled: false,
   });

-  setTimeout(() => {
+  await setTimeout(100);
     assert.throws(
       () => {
         nsolid.heapProfile();
       },
       {
         message: 'Heap profile could not be started'
       }
     );
     // ... rest of assertions
-  }, 100);
 }

This would improve readability and make the test more maintainable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 987f908 and da96906.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (4 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (2 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🔇 Additional comments (32)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

179-226: Verify synchronization guarantees for config changes.

The test uses setImmediate() to sequence operations after calling nsolid.start(), enableAssets(), and disableAssets(). However, there's no explicit guarantee that config changes have fully propagated before the next assertion executes. This could lead to flaky test failures if the timing assumptions don't hold in all environments.

Consider one of the following approaches:

  1. If nsolid provides synchronous config updates or completion callbacks, use those instead of setImmediate().
  2. Add explicit verification that the config state has changed before proceeding (e.g., check a status flag).
  3. Document the timing assumptions if setImmediate() is sufficient based on the nsolid implementation.

Run the following script to check if other nsolid tests use similar patterns:

agents/zmq/src/zmq_agent.h (1)

731-731: LGTM! Atomic flag added for thread-safe asset control.

The assets_enabled_ flag is appropriately declared as std::atomic<bool> for thread-safe access and is correctly placed in the profiling section alongside related state variables.

agents/grpc/src/grpc_errors.h (1)

12-13: LGTM! New error type correctly defined.

The EAssetsDisabled error entry is properly added with:

  • Sequential error code (1008)
  • Appropriate HTTP status (500) for internal/configuration errors
  • Clear error message that aligns with test expectations
agents/zmq/src/zmq_agent.cc (2)

1282-1287: LGTM! Configuration reading follows established pattern.

The assetsEnabled configuration is correctly read from the config and stored in the atomic flag, following the same pattern used for other configuration fields.


2115-2117: LGTM! Asset gating correctly implemented.

The early return when assets_enabled_ is false properly prevents profiling operations when assets are disabled. The UV_EINVAL error code maps to "Invalid arguments" (code 422) in create_command_error, which is appropriate for this validation failure.

test/agents/test-grpc-tracing.mjs (3)

5-5: LGTM! Import added for async delays in test flow.

The delay import from node:timers/promises is appropriately added to support the new phase-based test flow that requires waiting for spans to flush between enable/disable cycles.


205-247: LGTM! Comprehensive phase-based test for HTTP tracing.

The test correctly implements a multi-phase flow to verify:

  1. Initial span collection works
  2. Disabling traces prevents span collection (verified with delay and assertion)
  3. Re-enabling traces restores span collection

The phase state machine and early return on 'done' prevent race conditions and ensure proper test sequencing.


273-315: LGTM! Comprehensive phase-based test for custom tracing.

The custom tracing test mirrors the HTTP tracing test structure, providing consistent coverage for the disable/enable functionality across different tracing types.

lib/nsolid.js (8)

133-136: LGTM! Public API surface extended with asset and trace controls.

The four new functions (enableAssets, disableAssets, enableTraces, disableTraces) are appropriately exported and provide runtime control over asset collection and tracing.


195-206: LGTM! Getters provide appropriate defaults.

The new getters correctly:

  • Default assetsEnabled to true (assets enabled by default)
  • Default tracingEnabled to false (tracing disabled by default)
  • Use nullish coalescing (??) to handle undefined config values

413-415: LGTM! Assets guard correctly prevents heap profiling when disabled.

The early check throws ERR_NSOLID_HEAP_PROFILE_START when assets are disabled, preventing the operation before any profiling setup occurs.


497-498: LGTM! Assets guard correctly prevents heap sampling when disabled.

The early check throws ERR_NSOLID_HEAP_SAMPLING_START when assets are disabled, consistent with the heap profiling guard.


604-633: LGTM! Enable/disable functions correctly check state before updating.

All four functions follow a consistent pattern:

  1. Check current state to avoid unnecessary updates
  2. Call updateConfig with the new value

This prevents redundant configuration updates and ensures idempotent behavior.


761-765: LGTM! Config update handling uses normalization helper.

The assetsEnabled configuration is correctly normalized using optionToBool before storing, which handles various input types (boolean, number, string) consistently.


947-956: LGTM! Config initialization with proper defaults.

The assetsEnabled field is correctly initialized with:

  1. Priority order: environment variable > package.json > default (true)
  2. Proper normalization using optionToBool
  3. Sensible default of true when neither source provides a value

1106-1116: LGTM! Flexible normalization helper for boolean configuration.

The optionToBool function appropriately handles:

  • undefined/nullundefined (allows detecting absence of value)
  • Boolean → direct return
  • Number → falsy check (0 → false, non-zero → true)
  • String → delegates to envToBool for "0"/"false" handling
  • Other types → undefined (graceful handling)
agents/grpc/proto/reconfigure.proto (1)

20-20: LGTM! Protocol buffer field correctly defined.

The assetsEnabled field is properly added with:

  • Sequential field number (13)
  • Optional modifier for backward compatibility
  • Appropriate boolean type
  • Consistent camelCase naming
test/common/nsolid-zmq-agent/client.js (1)

141-148: LGTM! IPC message handlers correctly wire up runtime controls.

The four new message handlers properly:

  • Call the corresponding nsolid functions to enable/disable assets and traces
  • Follow the fire-and-forget pattern (no response sent)
  • Are consistent with the existing message handler structure
agents/grpc/src/grpc_agent.h (1)

352-352: LGTM! Atomic flag added for thread-safe asset control.

The assets_enabled_ flag is appropriately declared as std::atomic<bool> for thread-safe access and is correctly placed in the profiling section alongside related profiling state.

test/parallel/test-nsolid-config-tracing.js (1)

24-29: LGTM: runtime tracing helpers keep config in sync

The toggles and corresponding assertions look correct and consistent with earlier start() checks.

test/agents/test-zmq-profile.mjs (1)

120-177: LGTM: ZMQ profile respects assetsEnabled and event flow

Good coverage of disabled → 422 path, enabled happy-path, and final config verification. Resolve guard avoids double-resolve.

test/parallel/test-nsolid-heap-profile-stream.js (1)

68-70: LGTM: deterministic heap profile stream toggling

Nice sequencing around ‘end’ to avoid races; assertions match the API’s disabled vs enabled behavior.

Also applies to: 72-126

test/agents/test-zmq-heap-profile.mjs (1)

342-408: LGTM: ZMQ heap-profile honors assetsEnabled and validates payload

Covers error path, success path, and final config sync. Bootstrap opts prevent stray events. Looks good.

test/agents/test-zmq-snapshot.mjs (1)

235-296: LGTM: ZMQ snapshot gating via assetsEnabled

Error and success paths validated with proper event sequencing and final config check. All good.

test/parallel/test-nsolid-profile.js (1)

107-111: LGTM: assertion formatting

No behavior change; readability improved.

test/agents/test-zmq-heap-sampling.mjs (1)

135-201: LGTM! Comprehensive test for assetsEnabled toggling.

The test properly validates:

  • Disabling assets via config results in error code 422
  • Re-enabling assets allows profiling to proceed
  • Final config state is verified

The use of the resolved flag (lines 170-171) prevents duplicate event processing, which is a good defensive pattern.

test/agents/test-grpc-heap-sampling.mjs (1)

248-378: LGTM! Comprehensive coverage of asset control methods.

The three new tests thoroughly validate asset gating across different control surfaces:

  1. Environment variable (NSOLID_ASSETS_ENABLED)
  2. Configuration API (child.config())
  3. Helper methods (enableAssets/disableAssets)

Each test properly verifies:

  • Error 500 with message "Assets collection disabled(1008)" when disabled
  • Successful profiling when enabled
  • Final config state consistency

The pattern is consistent and maintainable.

test/agents/test-grpc-snapshot.mjs (1)

246-367: LGTM! Consistent test coverage for snapshot operations.

The tests mirror the heap sampling pattern and properly validate snapshot behavior with asset controls. The consistency across test files makes the test suite easier to understand and maintain.

test/agents/test-grpc-heap-profile.mjs (1)

259-389: LGTM! Complete test coverage for heap profiling with asset controls.

The tests maintain consistency with the other asset control tests and properly validate heap profiling behavior across all three control surfaces.

test/common/nsolid-zmq-agent/index.js (1)

187-233: LGTM! Clean helper methods for runtime control.

The four new methods (enableTraces, disableTraces, enableAssets, disableAssets) follow the established async pattern in the TestClient class. The implementation is straightforward and consistent.

test/common/nsolid-grpc-agent/index.js (1)

346-401: LGTM! Excellent refactoring with the #sendAndWait helper.

The introduction of the #sendAndWait helper method (lines 389-401) is a good abstraction that:

  • Reduces code duplication
  • Provides a consistent pattern for message handling
  • Makes the new helper methods (enableAssets, disableAssets, enableTraces, disableTraces) more concise and maintainable

The refactored config() method now uses this helper, demonstrating its utility.

src/nsolid/nsolid_api.cc (1)

1161-1186: Verify continuous profiler is disabled when assetsEnabled is false.

The logic correctly gates contCpuProfile and contCpuProfileInterval reads behind the assetsEnabled check. However, ensure that update_continuous_profiler properly handles the case when both flags are false (which happens when assetsEnabled is false).

Run the following to verify the continuous profiler handling:

Comment thread test/agents/test-zmq-tracing.mjs
Comment thread test/parallel/test-nsolid-heap-sampling-stream.js
Comment thread test/parallel/test-nsolid-snapshot.js
@santigimeno santigimeno force-pushed the santi/assets_enabled branch 2 times, most recently from b3ccdf4 to 74e0d6b Compare October 14, 2025 09:15
Copy link
Copy Markdown

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

179-187: Also assert the error code for gating failures

When assets are disabled, heapSamplingStream throws ERR_NSOLID_HEAP_SAMPLING_START synchronously. Assert the code property too for stronger checks.

     assert.throws(
       () => {
         nsolid.heapSamplingStream(0, 1000);
       },
       {
-        message: 'Heap sampling could not be started'
+        message: 'Heap sampling could not be started',
+        code: 'ERR_NSOLID_HEAP_SAMPLING_START'
       }
     );

Apply the same change to Lines 204‑211.

Also applies to: 204-211

🧹 Nitpick comments (10)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

173-177: Avoid starting ZMQ in this unit test

No need to pass command/data to nsolid.start() here. It can introduce unwanted ZMQ side-effects. Set only assetsEnabled.

-  nsolid.start({
-    command: 'localhost:9001',
-    data: 'localhost:9002',
-    assetsEnabled: false,
-  });
+  nsolid.start({ assetsEnabled: false });
lib/nsolid.js (3)

413-416: Validate args before gating; remove unused parameter

  • Move the assetsEnabled check after validateNumber/validateBoolean to preserve argument error semantics consistently.
  • The options parameter in heapProfileStream is unused; drop it to avoid confusion.
-function heapProfileStream(threadId, duration, trackAllocations, options) {
-  if (getConfig('assetsEnabled') === false)
-    throw new ERR_NSOLID_HEAP_PROFILE_START();
+function heapProfileStream(threadId, duration, trackAllocations) {
+  // Validate arguments first.
   validateNumber(threadId, 'threadId');
   validateNumber(duration, 'duration');
   validateBoolean(trackAllocations, 'trackAllocations');
+  // Gate after validation.
+  if (getConfig('assetsEnabled') === false)
+    throw new ERR_NSOLID_HEAP_PROFILE_START();

947-957: Initialize assetsEnabled from env/pkg with sane default

Solid defaulting logic. Be mindful that empty-string env values may not normalize to boolean (see optionToBool suggestion below).

Consider how NSOLID_ASSETS_ENABLED="" should behave. If it should be treated as “unset”, ensure optionToBool handles empty strings accordingly (see proposed change on Lines 1106‑1116).


1106-1116: optionToBool: handle empty strings explicitly and return strict booleans

If a string is empty, current envToBool returns a falsy value (empty string), not a strict boolean. When stored in config, later strict comparisons (=== false) can misbehave.

function optionToBool(value) {
  if (value === undefined || value === null)
    return undefined;
  if (typeof value === 'boolean')
    return value;
  if (typeof value === 'number')
    return value !== 0;
-  if (typeof value === 'string')
-    return envToBool(value);
+  if (typeof value === 'string') {
+    if (value === '')
+      return undefined; // treat empty string as “unset”
+    return value !== '0' && value.toLowerCase() !== 'false';
+  }
  return undefined;
}
test/parallel/test-nsolid-profile.js (1)

120-182: Reduce flakiness: replace fixed 100ms delays with platform-aware timeouts

Use common.platformTimeout(100) for the nested setTimeout calls to better tolerate slow CI hosts.

Apply this diff within runAssetsToggleTests():

-    }, 100);
+    }, common.platformTimeout(100));
...
-          }, 100);
+          }, common.platformTimeout(100));
...
-            }, 100);
+            }, common.platformTimeout(100));
...
-    }, 100);
+    }, common.platformTimeout(100));
test/agents/test-zmq-heap-profile.mjs (1)

342-409: Confirm error semantics across protocols

Here ZMQ returns 422 "Invalid arguments" when assets are disabled, while gRPC tests assert 500 "Assets collection disabled(1008)". Is this divergence intentional? If not, consider aligning error codes/messages for consistency across agents.

test/parallel/test-nsolid-heap-sampling.js (1)

177-248: Harden timeouts against CI variance

Use common.platformTimeout(100) instead of raw 100ms in nested setTimeout calls to reduce flakiness.

Suggested changes within runHeapSamplingAssetsToggleTests():

-            }, 100);
+            }, common.platformTimeout(100));
-          }, 100);
+          }, common.platformTimeout(100));
-    }, 100);
+    }, common.platformTimeout(100));
-  }, 100);
+  }, common.platformTimeout(100));
test/parallel/test-nsolid-heap-profile.js (1)

133-205: Use platform-aware timeouts to avoid flakes

Replace fixed 100ms in nested setTimeout calls with common.platformTimeout(100).

Apply within runHeapProfileAssetsToggleTests():

-            }, 100);
+            }, common.platformTimeout(100));
-          }, 100);
+          }, common.platformTimeout(100));
-    }, 100);
+    }, common.platformTimeout(100));
-  }, 100);
+  }, common.platformTimeout(100));
test/agents/test-grpc-snapshot.mjs (1)

282-324: Reconfigure propagation timing

After child.config({ assetsEnabled: false/true }), the test immediately calls heapSnapshot(). If reconfigure is eventually consistent, add a small wait or poll child.config() until it reflects the new value before proceeding to avoid rare races.

test/agents/test-grpc-continuous-profile.mjs (1)

275-432: Consider removing or documenting the commented-out code.

This ~160-line commented-out section contains three test cases. If these tests are obsolete, remove them to improve code clarity. If they're deferred for future work, consider opening an issue to track them and adding a brief comment explaining why they're commented out.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da96906 and 74e0d6b.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (4 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (2 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
  • test/common/nsolid-zmq-agent/client.js
  • agents/grpc/src/grpc_agent.cc
  • test/agents/test-zmq-snapshot.mjs
  • agents/grpc/src/grpc_agent.h
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-grpc-profile.mjs
  • agents/zmq/src/zmq_agent.h
  • agents/zmq/src/zmq_agent.cc
  • src/nsolid/nsolid_api.cc
  • test/parallel/test-nsolid-config-tracing.js
  • test/agents/test-grpc-heap-profile.mjs
  • test/common/nsolid-grpc-agent/index.js
  • agents/grpc/src/grpc_errors.h
  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-profile-stream.js
  • agents/grpc/src/proto/reconfigure.pb.h
🔇 Additional comments (21)
lib/nsolid.js (7)

133-137: Public toggles for assets/traces exposed

Good addition. Clear, minimal API with enable/disable helpers.


195-207: Config getters for assetsEnabled/tracingEnabled

Good defaults and exposure via nsolid.config and getters.


497-499: Gating for heapSamplingStream

Looks correct; early throw prevents stream creation when assets disabled.


761-765: assetsEnabled in updateConfig

Normalization via optionToBool and conditional assignment is correct.


604-618: enableAssets/disableAssets helpers

Simple, idempotent toggles are good.


620-634: enableTraces/disableTraces helpers

Consistent with assets toggles.


637-638: Doc nit

Minor doc change acknowledged. No issues.

agents/grpc/proto/reconfigure.proto (1)

20-20: Add assetsEnabled to ReconfigureBody

Looks good. Field number 13 follows existing numbering without conflict.

test/agents/test-zmq-profile.mjs (1)

120-177: ZMQ profile test for assetsEnabled toggle

Good coverage: disables via config, asserts 422, re-enables and validates profile flow; also verifies config state.

test/common/nsolid-grpc-agent/client.js (2)

64-69: Guard otel.register against duplicate registration

Good defensive handling; ignores expected already-registered error.


124-137: Support runtime toggle messages for assets/traces

Handlers invoke nsolid toggles and send acks. Looks correct.

test/parallel/test-nsolid-profile.js (1)

107-112: LGTM on assertion formatting and flow

Multi-line strictEqual improves readability, and invoking runAssetsToggleTests() extends coverage.

test/parallel/test-nsolid-heap-sampling.js (1)

164-168: ** LGTM on assertion formatting**

Message split across lines is fine and consistent with other tests.

test/parallel/test-nsolid-heap-profile.js (1)

124-124: LGTM

Running runHeapProfileAssetsToggleTests() extends coverage for assets toggling.

test/agents/test-grpc-snapshot.mjs (2)

246-280: LGTM: env-based gating coverage

Good validation of NSOLID_ASSETS_ENABLED=0 yielding 500 "Assets collection disabled(1008)".


325-368: LGTM: helper-based toggling

disableAssets()/enableAssets() paths look correct; asserting currentConfig.assetsEnabled ensures state consistency.

test/agents/test-grpc-heap-sampling.mjs (1)

248-378: LGTM! Comprehensive test coverage for assets gating.

The three new test cases thoroughly validate the assetsEnabled feature across different configuration methods (environment variable, config API, and helper methods). The tests correctly verify that heap sampling returns a 500 error when assets are disabled and succeeds when re-enabled.

test/agents/test-grpc-tracing.mjs (1)

5-5: LGTM! Well-structured phase-based testing for tracing toggle.

The phase-based state machine ('initial' → 'disabled' → 'reenabled' → 'done') effectively validates that tracing can be toggled at runtime. The tests correctly verify that no spans are emitted when tracing is disabled and that spans resume after re-enabling.

Also applies to: 205-247, 273-315

test/agents/test-grpc-continuous-profile.mjs (1)

4-4: LGTM! Thorough validation of continuous profiling with assets gating.

The new and modified tests effectively verify that continuous CPU profiling respects the assetsEnabled flag across different configuration methods. The use of profile counters and timing delays provides clear validation of the gating behavior.

Also applies to: 100-224

test/common/nsolid-zmq-agent/index.js (1)

187-233: LGTM! Helper methods follow established patterns.

The four new methods (enableTraces, disableTraces, enableAssets, disableAssets) correctly follow the existing IPC messaging pattern used throughout the TestClient class. The implementation is consistent with other helper methods.

agents/grpc/src/proto/reconfigure.pb.cc (1)

1-1082: Auto-generated protobuf code.

This file contains auto-generated code from the protocol buffer compiler. The changes mechanically add the assetsEnabled field (field 13) to ReconfigureBody with appropriate initialization, serialization, and accessor logic. No manual review required.

Copy link
Copy Markdown

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
test/agents/test-zmq-heap-sampling.mjs (1)

135-201: LGTM! Completes consistent coverage across asset types.

The test maintains the established pattern for heap sampling, with appropriate sampling-specific validations (lines 189-191). The consistent implementation across all three asset types (snapshot, heap profile, heap sampling) demonstrates thorough test coverage for the assetsEnabled feature.

Note: The three tests share a nearly identical structure. While this duplication is acceptable and even beneficial for test clarity and isolation, consider extracting a reusable test helper if additional asset types are introduced in the future. For example:

async function testAssetsEnabledToggle(playground, startAssetFn, validateAssetFn) {
  // Common toggle logic
}

However, this refactor is entirely optional given the current scope.

test/parallel/test-nsolid-snapshot.js (3)

102-104: Confirm mustSucceed() without handler; otherwise pass a noop for safety

If common.mustSucceed() requires a function, this could throw. Passing a noop keeps semantics identical and avoids coupling to helper internals.

-              nsolid.snapshot(common.mustSucceed());
+              nsolid.snapshot(common.mustSucceed(() => {}));

60-66: Re-invoking nsolid.start: verify idempotency and side-effects

Starting twice in the same process may reinitialize state/transport. If not guaranteed safe, prefer a single start then drive behavior via enableAssets()/disableAssets() and disableSnapshots flags, or reconfigure if supported.


68-76: Strengthen assertions: check the specific error code when assets are disabled

This PR introduces EAssetsDisabled. Asserting the code (in addition to message) avoids conflating “assets disabled” with other failure causes.

Example tweaks:

-    assert.throws(
+    assert.throws(
       () => {
         nsolid.snapshot();
       },
       {
-        message: 'Heap snapshot could not be generated'
+        message: 'Heap snapshot could not be generated',
+        code: 'EAssetsDisabled'
       }
     );
-    assert.notStrictEqual(err.code, 0);
-    assert.strictEqual(err.message, 'Heap snapshot could not be generated');
+    assert.strictEqual(err.code, 'EAssetsDisabled');
+    assert.strictEqual(err.message, 'Heap snapshot could not be generated');

Apply similarly to the later assert. If codes can vary by transport, consider allowing either the new code or falling back to the message match.

Also applies to: 78-81, 90-97

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74e0d6b and de122d2.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (4 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (2 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • agents/zmq/src/zmq_agent.h
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-zmq-profile.mjs
  • agents/grpc/src/grpc_agent.h
  • agents/grpc/src/grpc_agent.cc
  • test/agents/test-grpc-heap-profile.mjs
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-grpc-agent/client.js
  • agents/grpc/proto/reconfigure.proto
  • test/common/nsolid-zmq-agent/client.js
  • test/parallel/test-nsolid-profile.js
  • agents/zmq/src/zmq_agent.cc
  • src/nsolid/nsolid_api.cc
  • test/parallel/test-nsolid-heap-sampling-stream.js
  • agents/grpc/src/grpc_errors.h
  • test/common/nsolid-zmq-agent/index.js
  • test/agents/test-grpc-snapshot.mjs
  • test/agents/test-grpc-profile.mjs
🔇 Additional comments (36)
test/parallel/test-nsolid-heap-profile.js (2)

124-124: LGTM!

The placement and invocation of the new test function is appropriate, ensuring it runs after the existing test flow completes.


133-204: Verify enableAssets/disableAssets return values and consider timing robustness.

The test provides good coverage of asset toggle scenarios. However, consider these improvements:

  1. Missing verification: The calls to enableAssets() and disableAssets() (lines 166, 172, 193) don't verify success. If these operations can fail or return status, add assertions to confirm the state changes actually occurred.

  2. Timing robustness: The test relies on multiple hardcoded 100ms timeouts (lines 141, 167, 173, 194). In slow CI environments or under load, these may be insufficient and cause flakiness. Consider whether these delays can be replaced with event-driven synchronization or if they need to be more generous.

  3. Callback nesting: The deeply nested structure (5 levels deep starting at line 169) reduces readability. While it follows the existing pattern in this file, consider whether this could be flattened in a future refactor.

To verify the reliability of the timing assumptions, you could run this test repeatedly under load:

test/agents/test-zmq-snapshot.mjs (1)

235-296: LGTM! Well-structured test for runtime asset toggling.

The test correctly validates the assetsEnabled runtime control:

  • Properly disables assets and verifies the expected 422 error when attempting a snapshot
  • Re-enables assets and confirms the snapshot succeeds
  • Verifies the final configuration state

The defensive resolved flag (lines 242, 269-270) appropriately prevents duplicate event processing after test completion.

test/agents/test-zmq-heap-profile.mjs (1)

342-408: LGTM! Consistent implementation for heap profiling.

The test follows the same proven pattern as the snapshot test and includes appropriate heap-profile-specific validations:

  • Lines 396-398 correctly verify that trackAllocations: false results in empty trace data
  • Runtime toggling flow is properly implemented with correct error handling
  • Final state verification confirms assetsEnabled persists after operation completion
agents/grpc/src/proto/reconfigure.pb.cc (6)

49-50: LGTM! Generated field initialization is correct.

The new assetsenabled_ field is properly initialized to false in the constexpr constructor, following the same pattern as other boolean fields like contcpuprofile_.


107-133: LGTM! Has-bit layout correctly updated.

The has-bit index offset (line 107) and field offsets (lines 120, 133) are properly updated to accommodate the new assetsenabled_ field. The hasbit index of 11 for the new field is consistent with it being the 12th optional field (0-indexed).


243-245: LGTM! Memory copy ranges correctly extended.

The copy constructor (lines 243-245) and SharedCtor (lines 263-265) memory ranges are properly updated to include the new assetsenabled_ field as the last member in the contiguous memory block.

Also applies to: 263-265


465-468: LGTM! Clear and serialization logic properly updated.

The Clear operation (lines 465-468) now uses the correct has-bits mask 0x00000f00U (covering bits 8-11), and the serialization code (lines 581-586) correctly emits the new field with tag 13 when present.

Also applies to: 581-586


624-624: LGTM! Size calculation and merge logic correctly updated.

Line 624: The has-bits mask in ByteSizeLong is correctly updated from 0x000005e0U to 0x00000de0U, adding bit 11 for the new field.

Lines 702-714: The MergeImpl function properly handles merging the new assetsenabled_ field when has-bit 0x00000800U is set.

Also applies to: 702-714


739-740: LGTM! Swap logic correctly updated.

The InternalSwap range (lines 739-740) now correctly uses assetsenabled_ as the end marker since it's the last field in the contiguous memory block, replacing the previous end marker contcpuprofile_.

agents/grpc/src/proto/reconfigure.pb.h (4)

233-233: LGTM! Field number and parse table correctly defined.

Line 233: The new field number kAssetsEnabledFieldNumber = 13 is correctly defined in the enum.

Line 408: The TcParseTable template parameter is properly updated from <4, 12, ...> to <4, 13, ...> to reflect the increased field count.

Also applies to: 408-408


392-401: LGTM! Public accessor declarations are complete.

Lines 392-401 declare the standard protobuf accessors for the assetsEnabled field:

  • Public: has_assetsenabled(), clear_assetsenabled(), assetsenabled(), set_assetsenabled()
  • Private: _internal_assetsenabled(), _internal_set_assetsenabled()

All follow the standard protobuf API patterns.


442-442: LGTM! Field added to internal struct.

Line 442: The assetsenabled_ field is correctly added as the last boolean member in the Impl_ struct, maintaining consistency with the implementation file.


1175-1201: LGTM! Accessor implementations are complete and correct.

Lines 1175-1201 provide the inline implementations for all assetsEnabled accessors. The implementation:

  • Uses the correct has-bit mask 0x00000800U (bit 11)
  • Includes proper TSan annotations for thread safety
  • Follows the exact pattern of other boolean fields

The generated code is mechanically correct and consistent.

test/parallel/test-nsolid-config-tracing.js (1)

23-29: LGTM!

The test properly validates that the new runtime tracing toggle helpers (disableTraces() and enableTraces()) correctly update the config.tracingEnabled state.

test/agents/test-grpc-tracing.mjs (2)

205-247: Well-structured phase-based testing approach.

The introduction of a phase state machine ('initial''disabled''reenabled''done') properly orchestrates the multi-step tracing toggle test. The 200ms delays ensure spans have time to emit (or not emit when disabled), and the phase guards prevent race conditions after test completion.


273-315: LGTM!

The custom tracing test follows the same solid phase-based pattern as the HTTP tracing test, ensuring consistent validation of the trace enable/disable functionality.

test/parallel/test-nsolid-heap-sampling.js (2)

164-169: LGTM!

The multi-line assertion formatting improves readability, and the subsequent call to runHeapSamplingAssetsToggleTests() properly integrates the new test flow.


177-248: Comprehensive asset toggle testing.

The test function thoroughly validates heap sampling behavior when assets are toggled:

  1. Disables assets and verifies errors are thrown
  2. Re-enables assets and confirms sampling works
  3. Disables again and re-validates error conditions

The timing and sequencing properly ensure each operation completes before the next begins.

test/agents/test-grpc-heap-sampling.mjs (3)

248-285: LGTM!

This test properly validates that heap sampling respects the NSOLID_ASSETS_ENABLED environment variable, returning the expected error when assets are disabled.


287-331: LGTM!

The test correctly validates toggling assetsEnabled via nsolid.start() configuration, verifying both the disabled error state and the successful profile data collection when re-enabled.


333-378: LGTM!

This test properly exercises the runtime helper methods (disableAssets() and enableAssets()), confirming they gate heap sampling operations correctly and update the configuration state.

test/agents/test-grpc-continuous-profile.mjs (4)

100-135: LGTM!

The test properly validates that continuous profiling is suppressed when NSOLID_ASSETS_ENABLED='0', waiting an appropriate duration to confirm no profiles are emitted.


137-180: LGTM!

This test correctly validates that continuous profiling stops/resumes when assetsEnabled is toggled via configuration, with appropriate delays to observe profile emission patterns.


182-224: LGTM!

The test properly exercises the runtime helpers (disableAssets()/enableAssets()) with continuous profiling, confirming that profile emission is gated correctly and configuration state is updated.


275-432: Verify the intent of the extensive commented code.

There are 157 lines of commented-out test code. Please confirm whether:

  1. This code is intentionally disabled for future use
  2. These tests are superseded by the active tests
  3. The commented code should be removed

If this is intentional technical debt or work-in-progress, consider adding a comment explaining the rationale.

test/common/nsolid-grpc-agent/index.js (2)

346-387: Well-designed API extensions for runtime testing.

The new TestClient methods provide a clean interface for testing asset and tracing toggles:

  • Consistent early returns when #child is null
  • Unified #sendAndWait helper eliminates code duplication
  • The config() method's result mapping properly extracts the payload

389-401: LGTM!

The private #sendAndWait helper properly centralizes the request/response pattern with:

  • Configurable message type expectations
  • Optional result mapping
  • Proper listener cleanup after response
lib/nsolid.js (7)

133-136: LGTM!

The new runtime toggle functions (enableAssets, disableAssets, enableTraces, disableTraces) are properly exported on the public API surface.


195-206: LGTM!

The new getters expose the current asset and tracing states with appropriate defaults (assetsEnabled: true, tracingEnabled: false), using nullish coalescing to handle missing config values.


413-416: Proper runtime gating for asset collection.

Both heapProfileStream and heapSamplingStream correctly check assetsEnabled and throw the appropriate error when disabled, preventing expensive profiling operations at runtime.

Also applies to: 496-499


604-633: Well-implemented toggle functions.

All four toggle functions (enableAssets, disableAssets, enableTraces, disableTraces) follow a consistent pattern:

  1. Check current state to avoid unnecessary updates
  2. Call updateConfig with the new state

This ensures idempotent behavior and proper config propagation.


761-765: LGTM!

The updateConfig function properly handles the assetsEnabled option using optionToBool for normalization, ensuring only valid boolean values are assigned.


947-956: Proper configuration initialization for assetsEnabled.

The initialization correctly prioritizes environment variables over package.json settings, with a sensible default of true when neither is specified. This follows the established configuration precedence pattern.


1106-1116: Robust boolean normalization helper.

The optionToBool function properly handles various input types:

  • Returns undefined for null/undefined (preserving absence)
  • Passes through booleans unchanged
  • Converts numbers to boolean (0 = false, non-zero = true)
  • Uses envToBool for string conversion

This provides consistent boolean normalization across configuration sources.

test/parallel/test-nsolid-snapshot.js (1)

82-107: Good fix: toggles are now chained to snapshot completion, avoiding races

Sequencing enable/disable after the respective snapshot callbacks resolves the prior “in‑flight snapshot” race. This should make the test deterministic.

Copy link
Copy Markdown

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

Actionable comments posted: 3

🧹 Nitpick comments (8)
test/parallel/test-nsolid-profile.js (1)

120-182: Consider checking for asset-disabled-specific error codes or messages.

The test comprehensively exercises the asset toggle functionality, but all error assertions verify the generic message "CPU profile could not be started" without distinguishing whether the failure is specifically due to disabled assets versus other causes. Since the PR introduces an EAssetsDisabled error, consider adding more specific assertions to improve test precision and debugging.

For example, check for a specific error code when assets are disabled:

     assert.throws(
       () => {
         nsolid.profile();
       },
       {
+        code: 'ERR_NSOLID_ASSETS_DISABLED', // or whatever the actual code is
         message: 'CPU profile could not be started'
       }
     );

This would make test failures easier to diagnose and catch regressions where the wrong error type is returned.

test/parallel/test-nsolid-heap-profile-stream.js (2)

94-96: Minor: Inconsistent stream consumption pattern.

Lines 94-96 consume the stream by listening to 'data' events, while line 117 uses .resume() to consume the stream without listening to data. This inconsistency makes the test harder to follow.

Consider using a consistent pattern. If you don't need the profile data in the final test, use .resume() consistently:

-      let profile = '';
       const enabledStream = nsolid.heapProfileStream(0, 1200, true);
-      enabledStream.on('data', (chunk) => {
-        profile += chunk;
-      });
+      enabledStream.resume();
       enabledStream.on('end', common.mustCall(() => {
-        assert(JSON.parse(profile));

Also applies to: 117-117


119-119: Minor: Weak assertion.

assert.ok(true) is a tautology that doesn't validate any meaningful outcome. The test should either verify that the stream completed successfully (which common.mustCall on the 'end' handler already does) or be removed entirely.

Remove the redundant assertion:

 finalStream.on('end', common.mustCall(() => {
-  assert.ok(true);
 }));

Alternatively, if you want to verify stream completion, the presence of common.mustCall on the 'end' event handler is already sufficient validation.

test/parallel/test-nsolid-heap-sampling-stream.js (3)

192-200: Consider adding allocation activity before heap sampling.

The original test (lines 128-131) creates allocations to ensure heap sampling has data to sample. The re-enabled heap sampling tests might produce minimal or empty profiles without similar allocation activity.

If you want to ensure non-empty profiles in these tests, apply this diff:

     nsolid.enableAssets();
     setImmediate(() => {
+      const arr = [];
+      for (let i = 0; i < 1000; i++) {
+        arr.push(new Array(1000));
+      }
       let profile = '';

221-221: Remove the meaningless assertion.

The assert.ok(true) assertion doesn't validate anything meaningful. The common.mustCall wrapper already ensures the callback is invoked.

Apply this diff to remove the redundant assertion:

-            finalStream.on('end', common.mustCall(() => {
-              assert.ok(true);
-            }));
+            finalStream.on('end', common.mustCall());

180-188: Consider extracting the repeated error assertion pattern.

The error assertion pattern is duplicated twice. Extracting it into a helper function would reduce duplication and improve maintainability.

Consider refactoring like this:

function assertHeapSamplingDisabled() {
  assert.throws(
    () => {
      nsolid.heapSamplingStream(0, 1000);
    },
    {
      code: 'ERR_NSOLID_HEAP_SAMPLING_START',
      message: 'Heap sampling could not be started'
    }
  );
}

Then replace both assertion blocks with calls to assertHeapSamplingDisabled().

Also applies to: 205-213

test/parallel/test-nsolid-heap-profile.js (2)

130-132: Tight 100 ms waits may flake; prefer longer or state-driven waits

CI can be slower; 100 ms can race with config propagation. Consider increasing timeouts (or polling for state) to reduce flakes.

Apply this minimal diff to relax timing:

-  }, common.platformTimeout(100));
-}, common.platformTimeout(100));
+  }, common.platformTimeout(250));
+}, common.platformTimeout(250));

Optional: replace fixed sleeps with a small poll for the desired outcome to avoid magic delays:

async function waitForHeapProfileStartFailure() {
  // Poll until calling heapProfile() throws synchronously as expected.
  for (let i = 0; i < 25; i++) {
    try {
      nsolid.heapProfile();
    } catch (e) {
      assert.strictEqual(e.message, 'Heap profile could not be started');
      return;
    }
    await new Promise((r) => setTimeout(r, common.platformTimeout(20)));
  }
  assert.fail('heapProfile() did not start failing within the expected time');
}

Please verify on slow builders whether 100 ms is sufficient or adopt one of the above.


133-204: Solid toggle tests; add callback end-error checks and (optionally) “disable while active” case

Great coverage of disabled/enabled transitions. Two small gaps:

  • Also assert the callback form of heapProfileEnd() fails while disabled (mirrors start path).
  • Optionally, validate that disabling while an active profile is running still allows ending successfully (important runtime contract per PR).

Apply these small insertions to cover the callback end-error while disabled:

@@
     assert.throws(
       () => {
         nsolid.heapProfileEnd();
       },
       {
         message: 'Heap profile could not be stopped'
       }
     );
+    // Callback variant should also surface the disabled state
+    nsolid.heapProfileEnd(common.mustCall((err) => {
+      assert.notStrictEqual(err.code, 0);
+      assert.strictEqual(err.message, 'Heap profile could not be stopped');
+    }));
@@
             assert.throws(
               () => {
                 nsolid.heapProfileEnd();
               },
               {
                 message: 'Heap profile could not be stopped'
               }
             );
+            // Callback variant should also surface the disabled state
+            nsolid.heapProfileEnd(common.mustCall((err) => {
+              assert.notStrictEqual(err.code, 0);
+              assert.strictEqual(err.message, 'Heap profile could not be stopped');
+            }));

Optional addition to assert “disable while active” still allows end to succeed:

// Start a profile, disable assets while active, ensure end still succeeds.
nsolid.enableAssets();
setTimeout(() => {
  nsolid.heapProfile(common.mustSucceed(() => {
    nsolid.disableAssets();
    nsolid.heapProfileEnd(common.mustSucceed(() => {
      // Re-enable for any subsequent tests
      nsolid.enableAssets();
    }));
  }));
}, common.platformTimeout(100));

If the implementation surfaces a specific disabled error code (e.g., EAssetsDisabled), consider asserting it in these disabled paths to strengthen the check. Based on PR objectives.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de122d2 and 22ac97c.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (4 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
  • agents/grpc/proto/reconfigure.proto
  • test/agents/test-grpc-profile.mjs
  • test/agents/test-zmq-profile.mjs
  • agents/grpc/src/grpc_agent.cc
  • test/parallel/test-nsolid-config-tracing.js
  • test/common/nsolid-zmq-agent/client.js
  • src/nsolid/nsolid_api.cc
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-zmq-heap-profile.mjs
  • test/common/nsolid-zmq-agent/index.js
  • test/agents/test-grpc-snapshot.mjs
  • test/agents/test-zmq-heap-sampling.mjs
  • test/agents/test-grpc-heap-sampling.mjs
  • test/common/nsolid-grpc-agent/client.js
  • test/agents/test-zmq-snapshot.mjs
  • agents/grpc/src/grpc_agent.h
  • lib/nsolid.js
  • agents/zmq/src/zmq_agent.h
  • agents/zmq/src/zmq_agent.cc
🔇 Additional comments (24)
test/parallel/test-nsolid-profile.js (2)

107-111: LGTM!

The assertion formatting improves readability, and the integration of asset toggle testing at this point ensures all baseline profiling tests complete before exercising the new asset control surface.


117-118: LGTM!

Wrapping timeouts with common.platformTimeout() is a best practice that accounts for varying platform speeds and CI environments, reducing test flakiness.

test/parallel/test-nsolid-snapshot.js (3)

56-57: LGTM! Proper test integration.

The new test function is correctly integrated into the existing test flow, ensuring it runs after the previous assertions complete.


60-76: LGTM! Clear test setup and validation.

The test correctly configures nsolid with assets disabled but snapshots enabled, then validates that snapshot operations fail with the appropriate error message when assets are disabled.


78-108: LGTM! Race condition properly addressed.

The test correctly chains callbacks to ensure snapshots complete before toggling asset states, preventing the "Snapshot already in progress" race condition flagged in the previous review. The sequence properly:

  1. Waits for the error callback when assets are disabled (line 78)
  2. Enables assets only after the error is confirmed (line 83)
  3. Waits for the successful snapshot to complete before disabling assets (lines 86-88)
  4. Verifies the expected error after disabling (lines 90-97)
  5. Waits for the final snapshot to complete (line 103)

The use of common.mustSucceed() ensures all expected callbacks are invoked, providing deterministic test behavior.

test/parallel/test-nsolid-heap-profile-stream.js (1)

72-126: Major: Timing/synchronization concerns with nested setImmediate.

The test relies on nested setImmediate callbacks to ensure config changes (nsolid.start(), enableAssets(), disableAssets()) take effect before attempting operations. If these configuration updates are truly asynchronous or involve internal state propagation, a single setImmediate tick might be insufficient, leading to race conditions and flaky test behavior.

Verify whether the configuration methods are synchronous and whether setImmediate provides adequate synchronization:

test/parallel/test-nsolid-heap-sampling.js (4)

164-167: LGTM: Formatting improvement for readability.

The multi-line formatting of the error message assertion improves readability without changing the test logic.


174-175: Good: Consistent use of platform timeouts.

Using common.platformTimeout() ensures the test behaves correctly across different platform speeds and CI environments.


168-168: Good integration point for the new test.

Calling runHeapSamplingAssetsToggleTests() after the existing error path tests is a logical placement that extends the test coverage naturally.


177-248: Well-structured asset toggle test with timing considerations.

The test comprehensively covers the asset enable/disable scenarios for heap sampling:

  • Tests error paths when assets are disabled (both sync and async)
  • Properly sequences operations to avoid mid-sampling disruption (line 216 only disables after sampling completes)
  • Verifies successful sampling after re-enabling assets

However, the test relies heavily on setTimeout delays (100ms) for configuration changes to propagate. While common.platformTimeout() helps, this could still be a source of flakiness on heavily loaded systems.

Additionally, the test checks err.code !== 0 but doesn't verify the specific error code for the "assets disabled" scenario. According to the PR summary, an EAssetsDisabled error was added. Consider verifying the specific error code to make the test more precise and easier to debug if the wrong error is returned.

Run the following script to check if there's a specific error code constant for assets disabled that should be verified:

test/parallel/test-nsolid-heap-sampling-stream.js (1)

138-138: LGTM!

The integration point for the asset toggle tests is well-placed after validating the initial profile.

test/agents/test-grpc-tracing.mjs (2)

205-247: LGTM! Well-structured phase-based testing.

The multi-phase flow (initial → disabled → reenabled → done) effectively validates that tracing can be toggled at runtime and that spans are properly suppressed when tracing is disabled.


273-315: LGTM! Consistent test coverage.

The custom tracing test mirrors the http tracing test structure, ensuring comprehensive coverage of the enableTraces/disableTraces API across different span types.

test/agents/test-grpc-heap-profile.mjs (3)

259-296: LGTM! Proper error handling verification.

The test correctly validates that heap profiling returns a 500 error with code 1008 when assets collection is disabled via the environment variable.


298-342: LGTM! Comprehensive config toggle validation.

The test properly verifies that assetsEnabled can be toggled dynamically via nsolid.start() configuration and that the state changes affect profiling behavior as expected.


344-389: LGTM! Helper API coverage is complete.

The test validates the enableAssets/disableAssets convenience methods and confirms the final state with a config check, ensuring the API surface is working correctly.

agents/grpc/src/grpc_errors.h (1)

12-13: LGTM! Properly defined error type.

The new EAssetsDisabled error is correctly added to the GRPC_ERRORS macro with appropriate HTTP status code (500), message, and runtime code (1008) that aligns with the test expectations.

test/agents/test-grpc-continuous-profile.mjs (3)

121-156: LGTM! Proper verification of disabled continuous profiling.

The test correctly validates that continuous CPU profiles are not emitted when assets collection is disabled via environment variable, using an appropriate wait period to confirm no emissions.


158-201: LGTM! Dynamic toggle behavior is well-tested.

The test properly validates that continuous profiling can be stopped and restarted by toggling assetsEnabled via configuration, with appropriate tolerance for profiles in flight during state transitions.


203-245: LGTM! Helper methods are properly validated.

The test confirms that the disableAssets/enableAssets convenience methods correctly control continuous profiling behavior and verifies the final configuration state.

test/common/nsolid-grpc-agent/index.js (1)

346-401: LGTM! Well-structured test client API.

The new methods provide a clean interface for controlling assets and tracing at runtime. The centralized #sendAndWait helper promotes consistency and reduces duplication, with appropriate null checks for the child process.

agents/grpc/src/proto/reconfigure.pb.cc (1)

1-1082: Auto-generated protobuf code.

This file is automatically generated by the protocol buffer compiler from the proto definition. The changes correctly add support for the new assetsEnabled field (field 13) throughout the serialization, deserialization, copy, merge, and swap operations. The field is properly initialized to false and integrated into the has-bits mechanism.

agents/grpc/src/proto/reconfigure.pb.h (1)

233-233: Auto-generated protobuf header.

This file is automatically generated by the protocol buffer compiler. The changes correctly add the assetsEnabled field (field 13) with:

  • Public field number enum constant kAssetsEnabledFieldNumber = 13
  • Public accessors: has_assetsenabled(), clear_assetsenabled(), assetsenabled(), set_assetsenabled()
  • Private internal helpers: _internal_assetsenabled(), _internal_set_assetsenabled()
  • Private member variable assetsenabled_ in the Impl_ struct

The inline implementations follow standard protobuf patterns for optional boolean fields with proper has-bit management.

Also applies to: 392-401, 442-442, 1175-1201

test/parallel/test-nsolid-heap-profile.js (1)

124-124: Good placement for asset-toggle coverage

Running the toggle suite after the error-path chain is complete is sensible and avoids overlapping async state. LGTM.

Comment thread test/parallel/test-nsolid-heap-profile-stream.js
Comment thread test/parallel/test-nsolid-heap-profile-stream.js
Comment thread test/parallel/test-nsolid-heap-sampling-stream.js
Copy link
Copy Markdown

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
agents/grpc/src/grpc_errors.h (1)

12-13: New EAssetsDisabled enum looks correct; consider status code semantics.

Runtime code 1008 fits sequence; macro continuation is correct. Optionally consider HTTP-ish code 403 (forbidden) or 409 (conflict) instead of 500 to better signal operator-controlled disablement; align with clients/tests before changing.

Please confirm clients/tests map 1008 → “Assets collection disabled” and no other components expect 500 here.

agents/grpc/src/grpc_agent.cc (1)

1194-1199: Type‑safety when reading JSON; store atomically.

Guard against non-boolean values and prepare for atomic storage (see race comment below).

Apply:

-  {
-    auto it = config_.find("assetsEnabled");
-    if (it != config_.end()) {
-      assets_enabled_ = *it;
-    }
-  }
+  {
+    auto it = config_.find("assetsEnabled");
+    if (it != config_.end()) {
+      if (it->is_boolean()) {
+        assets_enabled_.store(*it, std::memory_order_relaxed);
+      } else {
+        Debug("Invalid assetsEnabled type; expected boolean\n");
+      }
+    }
+  }

Note: assets_enabled_ change assumes std::atomic (see next comment).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22ac97c and 8a2bb66.

📒 Files selected for processing (8)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • agents/zmq/src/zmq_agent.h
  • agents/grpc/src/grpc_agent.h
  • agents/zmq/src/zmq_agent.cc
  • agents/grpc/proto/reconfigure.proto
🧰 Additional context used
🧬 Code graph analysis (3)
agents/grpc/src/grpc_agent.cc (1)
agents/zmq/src/zmq_agent.cc (10)
  • config (430-476)
  • config (430-430)
  • config (1263-1378)
  • config (1263-1263)
  • config (2760-2795)
  • config (2760-2760)
  • config (2888-2905)
  • config (2888-2888)
  • config (2983-3000)
  • config (2983-2983)
agents/grpc/src/proto/reconfigure.pb.h (2)
agents/grpc/src/proto/command.pb.h (5)
  • google (42-49)
  • google (72-75)
  • internal (44-47)
  • ReconfigureBody (452-582)
  • ReconfigureBody (837-843)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
agents/grpc/src/proto/reconfigure.pb.cc (1)
agents/grpc/src/proto/reconfigure.pb.h (16)
  • ReconfigureBody (94-94)
  • ReconfigureBody (95-96)
  • ReconfigureBody (681-693)
  • ReconfigureBody (694-698)
  • ReconfigureBody (699-721)
  • ReconfigureBody (722-726)
  • ReconfigureBody (727-749)
  • ReconfigureBody (750-754)
  • ReconfigureBody (755-777)
  • ReconfigureBody (778-782)
  • ReconfigureBody (783-805)
  • ReconfigureBody (806-810)
  • ReconfigureBody (811-834)
  • ReconfigureBody (836-841)
  • ReconfigureBody (875-887)
  • ReconfigureBody (890-903)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: lint-js-and-md
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: build-tarball
  • GitHub Check: coverage-windows
  • GitHub Check: test-macOS
🔇 Additional comments (5)
agents/grpc/src/grpc_agent.cc (3)

395-398: Good: propagate assetsEnabled in ReconfigureEvent.

Only sets when present; matches ZMQ agent behavior.

Ensure reconfigure.proto field 13 is optional and absent when not set to avoid default false flips.


447-448: Defaulting assets to enabled is fine; confirm env/config precedence.

assets_enabled_ defaults true. Please confirm any NSOLID_ASSETS_ENABLED env or nsolid.start config is applied before first asset command to avoid a short window with the wrong default.


1755-1757: Good: include assetsEnabled in reconfigure output.

Keeps parity with ZMQ agent.

agents/grpc/src/proto/reconfigure.pb.h (1)

233-234: Generated field assetsEnabled = 13 is wired correctly.

Enum, accessors, has-bits (0x00000800U), and parse table size updates look consistent with protoc 6.32.0.

Please confirm these files were re-generated from the updated reconfigure.proto (no manual edits) and that build scripts pin protoc/runtime (6.32.0) to avoid cross-version CI drift.

Also applies to: 392-402, 408-446, 1175-1202

agents/grpc/src/proto/reconfigure.pb.cc (1)

49-51: Proto gencode changes align with assetsEnabled field; no manual edits suggested.

Init/clear/serialize/merge paths and descriptor/table deltas reflect field 13; has-bits masks updated (incl. 0x00000de0 popcount).

Ensure descriptor size/hash changes are committed wherever cached (if any), and that both pb.h/pb.cc are produced by the same protoc (6.32.0).

Also applies to: 107-121, 152-173, 338-429, 465-469, 581-587, 624-625, 702-715, 738-744

Comment thread agents/grpc/src/grpc_agent.cc
Copy link
Copy Markdown

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (4)
lib/nsolid.js (1)

392-416: Gating heapProfileStream when assets are disabled is correct; doc/param mismatch.

The early throw with ERR_NSOLID_HEAP_PROFILE_START is consistent with the intended synchronous-state checks. However, the function signature now includes an options argument that is neither documented nor used.

Consider one of:

  • Document options in the JSDoc (even if currently unused), or
  • Drop the unused parameter to avoid confusion.

Example doc tweak:

  /**
   * Starts a heap profile in a specific JS thread and returns a readable stream
   * of the profile data.
   * @param {number} threadId - The ID of the thread for which to start the heap profile.
   * @param {number} duration - The duration in milliseconds for which to run the heap profile.
   * @param {boolean} trackAllocations - Whether to track allocations during the heap profile.
+  * @param {object} [options] - Reserved for future use.

Based on learnings.

agents/grpc/src/grpc_agent.cc (3)

569-575: Off-thread JSON read in stop(): snapshot contCpuProfile to atomic.

stop(false-path) reads config_["contCpuProfile"] outside the agent loop. Replace with the atomic snapshot as above.

-    if (profile_stopped) {
-      auto it = config_.find("contCpuProfile");
-      if (it == config_.end() || it->get<bool>() == false) {
-        profile_on_exit_ = profile_stopped;
-      }
-    }
+    if (profile_stopped) {
+      if (!cont_cpu_profile_enabled_.load(std::memory_order_acquire)) {
+        profile_on_exit_ = true;
+      }
+    }

1487-1604: Continuous profiler ignores assetsEnabled at runtime.

got_continuous_profile() streams profiles even when assetsEnabled is false. PR objective says continuous profiler must respect assetsEnabled. Add a fast gate and close any open streams.

 void GrpcAgent::got_continuous_profile(
     const ProfileCollector::ProfileQStor& stor) {
+  if (!assets_enabled_.load(std::memory_order_acquire)) {
+    // Best-effort: close any existing stream for this thread and drop the data
+    auto it = cont_profile_stor_map_.find(std::visit([](auto& opt){ return opt.thread_id; }, stor.options));
+    if (it != cont_profile_stor_map_.end()) {
+      it->second.stream->WritesDone(true);
+      cont_profile_stor_map_.erase(it);
+    }
+    return;
+  }

2222-2235: Be consistent with JSON type checks for disableSnapshots.

Here disableSnapshots is read without is_boolean() check, unlike redactSnapshots. Align for safety.

-  auto it = config_.find("disableSnapshots");
-  if (it != config_.end()) {
-    disable_snapshots = *it;
-  }
+  auto it = config_.find("disableSnapshots");
+  if (it != config_.end()) {
+    if (it->is_boolean()) {
+      disable_snapshots = *it;
+    } else {
+      Debug("Invalid disableSnapshots type; expected boolean\n");
+    }
+  }
♻️ Duplicate comments (3)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

193-200: About missing error handlers on streams.

Noting prior discussion: adding enabledStream.on('error', ...) was suggested earlier and intentionally declined. Acknowledging that decision here to avoid duplication.

test/agents/test-zmq-tracing.mjs (1)

431-433: Good: immediate failure on spans while disabled.

The explicit assertion to fail if spans arrive in the 'disabled' phase addresses the earlier review concern.

agents/grpc/src/grpc_agent.cc (1)

2080-2097: Fix data races: make assetsEnabled and contCpuProfile checks atomic and stop reading JSON off-thread.

do_start_prof_init() is called from *from_js paths (non‑agent threads). It reads a non‑atomic assets_enabled and config_["contCpuProfile"] without synchronization. This is undefined behavior and can misgate profiling or crash under contention. Use atomics and snapshot contCpuProfile in config().

Apply these targeted changes:

@@
-  if (!assets_enabled_) {
+  // Off-thread read; use atomic load with acquire semantics
+  if (!assets_enabled_.load(std::memory_order_acquire)) {
     return ErrorType::EAssetsDisabled;
   }
@@
-  if (type == ProfileType::kCpu) {
-    auto it = config_.find("contCpuProfile");
-    if (it != config_.end() && it->get<bool>() == true) {
-      // Continuous CPU profiling is enabled, don't allow manual CPU profiles
-      return ErrorType::EInProgressError;
-    }
-  }
+  if (type == ProfileType::kCpu &&
+      cont_cpu_profile_enabled_.load(std::memory_order_acquire)) {
+    // Continuous CPU profiling is enabled, don't allow manual CPU profiles
+    return ErrorType::EInProgressError;
+  }

And in config() (see lines 1178–1183 and where contCpuProfile is handled) snapshot booleans into atomics:

@@
-  {
-    auto it = config_.find("assetsEnabled");
-    if (it != config_.end()) {
-      assets_enabled_ = *it;
-    }
-  }
+  {
+    auto it = config_.find("assetsEnabled");
+    if (it != config_.end()) {
+      if (it->is_boolean()) {
+        assets_enabled_.store(*it, std::memory_order_release);
+      } else {
+        Debug("Invalid assetsEnabled type; expected boolean\n");
+      }
+    }
+  }
+  {
+    auto it = config_.find("contCpuProfile");
+    if (it != config_.end()) {
+      if (it->is_boolean()) {
+        cont_cpu_profile_enabled_.store(*it, std::memory_order_release);
+      } else {
+        Debug("Invalid contCpuProfile type; expected boolean\n");
+        cont_cpu_profile_enabled_.store(false, std::memory_order_release);
+      }
+    } else {
+      cont_cpu_profile_enabled_.store(false, std::memory_order_release);
+    }
+  }

Header additions (agents/grpc/src/grpc_agent.h):

+#include <atomic>
@@
-  bool assets_enabled_;
+  std::atomic<bool> assets_enabled_{true};
+  std::atomic<bool> cont_cpu_profile_enabled_{false};
🧹 Nitpick comments (18)
lib/nsolid.js (1)

636-641: Minor JSDoc grammar nit.

“Resumes the process metrics collection when exiting. works only if called...” — sentence casing is off. Optional fix:

- * It resumes the process metrics collection when exiting.
- * works only if called from the
+ * Resumes the process metrics collection when exiting.
+ * Works only if called from the
test/parallel/test-nsolid-heap-sampling-stream.js (1)

171-228: Solid coverage for assets toggling; consider simplifying control flow.

The sequence thoroughly verifies disabled→enabled→disabled→enabled behavior and schema checks. The nested setImmediate chain is a bit hard to follow; consider extracting small helpers or flattening to improve readability.

If binding.updateConfig propagation is asynchronous, a single setImmediate might race on slow machines. If flakiness appears, gate with a small retry/assert loop on nsolid.config.assetsEnabled before invoking heapSamplingStream.

test/parallel/test-nsolid-snapshot.js (1)

68-108: Prefer platformTimeout over setImmediate to avoid propagation flakes

Reconfig (enable/disable) may take longer than a tick. Use common.platformTimeout(100) like other tests to stabilize.

-  setImmediate(() => {
+  setTimeout(() => {
     assert.throws(
       () => {
         nsolid.snapshot();
       },
       {
         message: 'Heap snapshot could not be generated'
       }
     );
 
     nsolid.snapshot(common.mustCall((err) => {
       assert.notStrictEqual(err.code, 0);
       assert.strictEqual(err.message, 'Heap snapshot could not be generated');
 
       // Only enable assets after the error callback completes
       nsolid.enableAssets();
-      setImmediate(() => {
+      setTimeout(() => {
         // Wait for snapshot to complete before disabling assets
         nsolid.snapshot(common.mustSucceed(() => {
           // Only disable assets after snapshot completes
           nsolid.disableAssets();
-          setImmediate(() => {
+          setTimeout(() => {
             assert.throws(
               () => {
                 nsolid.snapshot();
               },
               {
                 message: 'Heap snapshot could not be generated'
               }
             );
 
             // Only enable assets after error is confirmed
             nsolid.enableAssets();
-            setImmediate(() => {
+            setTimeout(() => {
               // Wait for final snapshot to complete
               nsolid.snapshot(common.mustSucceed());
-            });
-          });
+            }, common.platformTimeout(100));
+          }, common.platformTimeout(100));
         }));
-      });
+      }, common.platformTimeout(100));
     }));
-  });
+  }, common.platformTimeout(100));
test/parallel/test-nsolid-heap-profile.js (1)

141-163: Reduce duplication with a small helper for “assets disabled” assertions (optional)

The repeated assert.throws for start/stop can be centralized to improve readability.

+function assertHeapProfileDisabled() {
+  assert.throws(() => nsolid.heapProfile(), { message: 'Heap profile could not be started' });
+  assert.throws(() => nsolid.heapProfileEnd(), { message: 'Heap profile could not be stopped' });
+}
...
-    assert.throws(
-      () => {
-        nsolid.heapProfile();
-      },
-      { message: 'Heap profile could not be started' }
-    );
+    assertHeapProfileDisabled();
...
-            assert.throws(
-              () => {
-                nsolid.heapProfile();
-              },
-              { message: 'Heap profile could not be started' }
-            );
-
-            assert.throws(
-              () => {
-                nsolid.heapProfileEnd();
-              },
-              { message: 'Heap profile could not be stopped' }
-            );
+            assertHeapProfileDisabled();

Also applies to: 173-199

test/parallel/test-nsolid-heap-sampling.js (1)

185-207: Optional: extract a helper to assert “sampling disabled”

Refactor repeated throws to a helper for clarity.

+function assertHeapSamplingDisabled() {
+  assert.throws(() => nsolid.heapSampling(), { message: 'Heap sampling could not be started' });
+  assert.throws(() => nsolid.heapSamplingEnd(), { message: 'Heap sampling could not be stopped' });
+}
...
-    assert.throws(
-      () => {
-        nsolid.heapSampling();
-      },
-      { message: 'Heap sampling could not be started' }
-    );
+    assertHeapSamplingDisabled();
...
-            assert.throws(
-              () => {
-                nsolid.heapSampling();
-              },
-              { message: 'Heap sampling could not be started' }
-            );
-
-            assert.throws(
-              () => {
-                nsolid.heapSamplingEnd();
-              },
-              { message: 'Heap sampling could not be stopped' }
-            );
+            assertHeapSamplingDisabled();

Also applies to: 217-243

test/parallel/test-nsolid-profile.js (1)

152-177: Optional: also assert profileEnd() error after second disable

For parity with other suites, add a stop assertion when assets are disabled.

           setTimeout(() => {
             assert.throws(
               () => {
                 nsolid.profile();
               },
               {
                 message: 'CPU profile could not be started'
               }
             );
 
+            assert.throws(
+              () => {
+                nsolid.profileEnd();
+              },
+              {
+                message: 'CPU profile could not be stopped'
+              }
+            );
+
             // Only re-enable after error is confirmed
             nsolid.enableAssets();
test/agents/test-grpc-profile.mjs (1)

322-364: Add a test toggling assets to false during an active profile (recommended)

To guarantee in‑flight profiles aren’t aborted by reconfigure, start a profile, then disable assets, assert the ongoing one completes and subsequent starts return 500.

+tests.push({
+  name: 'should not abort an in-flight profile when assets are disabled mid-run',
+  test: async (getEnv) => {
+    return new Promise((resolve) => {
+      const grpcServer = new GRPCServer();
+      grpcServer.start(mustSucceed(async (port) => {
+        const env = getEnv(port);
+        const child = new TestClient([], { stdio: ['inherit','inherit','inherit','ipc'], env });
+        const agentId = await child.id();
+        const opts = { duration: 200, threadId: 0 };
+
+        const first = grpcServer.cpuProfile(agentId, opts); // start
+        await child.disableAssets(); // toggle off mid-run
+
+        // in-flight should still resolve with data
+        const { data, requestId } = await first;
+        checkProfileData(data.msg, data.metadata, requestId, agentId, opts, true);
+
+        // new starts should be rejected
+        const disabled = await grpcServer.cpuProfile(agentId, opts);
+        checkProfileError(disabled.data.msg, disabled.data.metadata, disabled.requestId,
+                          agentId, 500, 'Assets collection disabled(1008)');
+
+        await child.shutdown(0);
+        grpcServer.close();
+        resolve();
+      }));
+    });
+  },
+});
test/agents/test-grpc-snapshot.mjs (1)

246-280: Harden toggle tests against races and brittle string matches.

  • Verify assetsEnabled state right after toggles before issuing the RPC to avoid timing races.
  • Match the error message more defensively (keep code strict; loosen message equality).
-const { data, requestId } = await grpcServer.heapSnapshot(agentId, options);
+// ensure disabled took effect before attempting
+const cfg = await child.config();
+assert.strictEqual(cfg.assetsEnabled, false);
+const { data, requestId } = await grpcServer.heapSnapshot(agentId, options);
-checkSnapshotError(disabledResult.data.msg,
-                   disabledResult.data.metadata,
-                   disabledResult.requestId,
-                   agentId,
-                   500,
-                   'Assets collection disabled(1008)');
+checkSnapshotError(
+  disabledResult.data.msg,
+  disabledResult.data.metadata,
+  disabledResult.requestId,
+  agentId,
+  500,
+  disabledResult.data.msg.common.error.message // rely on server; assert code below
+);
+assert.ok(
+  disabledResult.data.msg.common.error.message.includes('(1008)'),
+  'Expected EAssetsDisabled (1008)'
+);

If you prefer to keep equality, at least assert the numeric code and use includes for the suffix to reduce brittleness. Please confirm that child.disableAssets() and child.enableAssets() return after the setting is applied; if not, the added child.config() check is needed. [Based on learnings]

Also applies to: 282-323, 325-367

test/agents/test-grpc-heap-sampling.mjs (1)

248-285: Stabilize assets gating assertions (avoid races, reduce brittleness).

  • After disabling via config/helpers, fetch child.config() and assert assetsEnabled === false before calling heapSampling.
  • Keep strict on HTTP status/code, but use .includes('(1008)') for the message to avoid brittle equality.
-const disabledResult = await grpcServer.heapSampling(agentId, profileOpts);
+const cfg = await child.config();
+assert.strictEqual(cfg.assetsEnabled, false);
+const disabledResult = await grpcServer.heapSampling(agentId, profileOpts);
-checkProfileError(disabledResult.data.msg,
-                  disabledResult.data.metadata,
-                  disabledResult.requestId,
-                  agentId,
-                  500,
-                  'Assets collection disabled(1008)');
+checkProfileError(
+  disabledResult.data.msg,
+  disabledResult.data.metadata,
+  disabledResult.requestId,
+  agentId,
+  500,
+  disabledResult.data.msg.common.error.message
+);
+assert.ok(
+  disabledResult.data.msg.common.error.message.includes('(1008)'),
+  'Expected EAssetsDisabled (1008)'
+);

Also applies to: 287-331, 333-378

test/agents/test-grpc-continuous-profile.mjs (4)

121-156: Consider a slightly longer quiet window to reduce CI flakes.

Twice the interval plus margin (e.g., 350–500ms) is safer than 300ms under load.

-        await setTimeout(300);
+        await setTimeout(400); // ~4×100ms intervals incl. jitter

158-201: Verify toggle applied and use adaptive assertions.

  • Confirm assetsEnabled === false after child.config({ assetsEnabled: false }) before measuring emissions.
  • Use a time window of ≥3 intervals to observe cessation/restart.
-        await child.config({ assetsEnabled: false });
+        const disabledCfg = await child.config({ assetsEnabled: false });
+        assert.strictEqual(disabledCfg.assetsEnabled, false);
@@
-        await setTimeout(250);
+        await setTimeout(350);
@@
-        await child.config({ assetsEnabled: true });
+        const enabledCfg = await child.config({ assetsEnabled: true });
+        assert.strictEqual(enabledCfg.assetsEnabled, true);
-        await setTimeout(250);
+        await setTimeout(350);

203-245: Do the same verification for enableAssets()/disableAssets().

Add config checks after each toggle and modestly increase windows.

-        await child.disableAssets();
+        await child.disableAssets();
+        const cfg1 = await child.config();
+        assert.strictEqual(cfg1.assetsEnabled, false);
@@
-        await setTimeout(250);
+        await setTimeout(350);
@@
-        await child.enableAssets();
+        await child.enableAssets();
+        const cfg2 = await child.config();
+        assert.strictEqual(cfg2.assetsEnabled, true);
-        await setTimeout(250);
+        await setTimeout(350);

429-449: Precondition check: ensure continuous profiling is enabled.

Before attempting the manual CPU profile conflict, assert the runtime config has continuous profiling enabled to avoid environment coupling.

-        // Wait for continuous profiling to start
-        await setTimeout(400);
+        // Wait for continuous profiling to start and verify it's enabled
+        await setTimeout(400);
+        const cfg = await child.config();
+        assert.strictEqual(cfg.contCpuProfile, true);
test/agents/test-grpc-heap-profile.mjs (1)

259-390: Make assets gating tests race‑resistant and less brittle on messages.

  • Assert assetsEnabled post‑toggle before issuing heapProfile.
  • Keep strict on status/error code; relax message match to .includes('(1008)').
-const { data, requestId } = await grpcServer.heapProfile(agentId, options);
+const cfg = await child.config();
+assert.strictEqual(cfg.assetsEnabled, false);
+const { data, requestId } = await grpcServer.heapProfile(agentId, options);
-checkProfileError(disabledResult.data.msg,
-                  disabledResult.data.metadata,
-                  disabledResult.requestId,
-                  agentId,
-                  500,
-                  'Assets collection disabled(1008)');
+checkProfileError(
+  disabledResult.data.msg,
+  disabledResult.data.metadata,
+  disabledResult.requestId,
+  agentId,
+  500,
+  disabledResult.data.msg.common.error.message
+);
+assert.ok(
+  disabledResult.data.msg.common.error.message.includes('(1008)'),
+  'Expected EAssetsDisabled (1008)'
+);
agents/grpc/src/grpc_agent.cc (1)

72-73: Nit: GRPC_MAX_SIZE comment is wrong.

4L10241024 is 4 MiB, not 4 GB. Fix the comment or the constant.

test/common/nsolid-grpc-agent/index.js (3)

357-379: Helper symmetry is good; consider returning ack for assertions.

enableAssets/disableAssets/enableTraces/disableTraces fire-and-wait but discard result. Returning a boolean ack would help tests assert success.


389-401: Add correlation and timeout to #sendAndWait to avoid cross-talk and hangs.

Multiple in-flight calls of the same type can race, and missing responses can hang forever. Include a requestId and a timeout.

Example change:

-#sendAndWait({ type, payload = {}, expect = type, map }) {
-  return new Promise((resolve) => {
-    const handler = (msg) => {
-      if (msg.type !== expect) return;
-      this.#child.off('message', handler);
-      resolve(map ? map(msg) : undefined);
-    };
-    this.#child.on('message', handler);
-    this.#child.send({ type, ...payload });
-  });
-}
+#sendAndWait({ type, payload = {}, expect = type, map, timeoutMs = 5000 }) {
+  return new Promise((resolve, reject) => {
+    const requestId = randomUUID();
+    const handler = (msg) => {
+      if (msg.type !== expect || msg.requestId !== requestId) return;
+      clearTimeout(timer);
+      this.#child.off('message', handler);
+      resolve(map ? map(msg) : undefined);
+    };
+    const timer = setTimeout(() => {
+      this.#child.off('message', handler);
+      reject(new Error(`Timeout waiting for ${expect}`));
+    }, timeoutMs);
+    this.#child.on('message', handler);
+    this.#child.send({ type, requestId, ...payload });
+  });
+}

Note: requires client.js to echo requestId. Do you want a follow-up patch for client.js?


381-387: Duplicate trace APIs; consider consolidating.

trace() and tracing() overlap. Prefer one name and deprecate the other to reduce confusion.

Also applies to: 593-603

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba2865a and d4fdf79.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (2 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • test/common/nsolid-zmq-agent/index.js
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-zmq-agent/client.js
  • test/agents/test-zmq-snapshot.mjs
  • test/agents/test-zmq-profile.mjs
  • src/nsolid/nsolid_api.cc
  • test/common/nsolid-grpc-agent/client.js
  • agents/grpc/proto/reconfigure.proto
  • agents/grpc/src/grpc_agent.h
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-grpc-tracing.mjs
  • agents/zmq/src/zmq_agent.cc
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-14T10:18:23.123Z
Learnt from: santigimeno
PR: nodesource/nsolid#377
File: test/parallel/test-nsolid-heap-profile-stream.js:81-88
Timestamp: 2025-10-14T10:18:23.123Z
Learning: In nsolid.heapProfileStream() and nsolid.heapSamplingStream(), synchronous throws occur for argument validation errors and immediate state validation checks (like assets being disabled), while async error events are emitted for runtime errors that manifest during execution (like invalid threadId).

Applied to files:

  • test/parallel/test-nsolid-heap-sampling-stream.js
  • lib/nsolid.js
🧬 Code graph analysis (15)
test/parallel/test-nsolid-heap-sampling.js (5)
test/common/nsolid-grpc-agent/index.js (2)
  • assert (4-4)
  • common (3-3)
test/common/nsolid-zmq-agent/index.js (2)
  • assert (4-4)
  • common (3-3)
lib/nsolid.js (2)
  • err (444-444)
  • err (532-532)
test/common/nsolid-grpc-agent/client.js (1)
  • nsolid (7-7)
test/common/nsolid-zmq-agent/client.js (1)
  • nsolid (6-6)
test/parallel/test-nsolid-profile.js (4)
test/common/nsolid-grpc-agent/index.js (2)
  • assert (4-4)
  • common (3-3)
test/parallel/test-nsolid-heap-profile.js (3)
  • assert (4-4)
  • common (3-3)
  • nsolid (5-5)
test/parallel/test-nsolid-heap-sampling.js (3)
  • assert (4-4)
  • common (3-3)
  • nsolid (5-5)
test/common/nsolid-zmq-agent/client.js (1)
  • nsolid (6-6)
test/agents/test-zmq-heap-sampling.mjs (1)
test/agents/test-zmq-heap-profile.mjs (19)
  • playground (491-491)
  • profile (43-43)
  • profile (98-98)
  • profile (252-252)
  • profile (298-298)
  • profile (347-347)
  • profile (415-415)
  • bootstrapOpts (51-54)
  • bootstrapOpts (105-109)
  • bootstrapOpts (259-262)
  • bootstrapOpts (304-308)
  • bootstrapOpts (356-359)
  • bootstrapOpts (426-429)
  • disabledConfig (362-362)
  • enabledConfig (373-373)
  • heapProf (75-75)
  • heapProf (133-133)
  • heapProf (395-395)
  • currentConfig (400-400)
test/agents/test-grpc-heap-sampling.mjs (2)
test/agents/test-grpc-heap-profile.mjs (63)
  • grpcServer (88-88)
  • grpcServer (113-113)
  • grpcServer (127-127)
  • grpcServer (154-154)
  • grpcServer (169-169)
  • grpcServer (183-183)
  • grpcServer (197-197)
  • grpcServer (218-218)
  • grpcServer (229-229)
  • grpcServer (252-252)
  • grpcServer (263-263)
  • grpcServer (283-283)
  • grpcServer (302-302)
  • grpcServer (333-333)
  • grpcServer (348-348)
  • grpcServer (377-377)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
  • opts (91-94)
  • opts (130-133)
  • opts (172-175)
  • opts (200-203)
  • opts (232-235)
  • opts (269-272)
  • opts (305-308)
  • opts (351-354)
  • opts (410-413)
  • opts (447-450)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • child (414-414)
  • child (451-451)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
  • profileOpts (311-317)
  • profileOpts (357-363)
  • disabledConfig (319-319)
  • disabledResult (322-322)
  • disabledResult (367-367)
  • enabledConfig (330-330)
  • currentConfig (380-380)
test/agents/test-grpc-profile.mjs (31)
  • env (80-80)
  • env (116-116)
  • env (154-154)
  • env (182-182)
  • env (214-214)
  • env (249-252)
  • opts (81-84)
  • opts (117-120)
  • opts (155-158)
  • opts (183-186)
  • opts (215-218)
  • opts (253-256)
  • child (85-85)
  • child (121-121)
  • child (159-159)
  • child (187-187)
  • child (220-220)
  • child (257-257)
  • agentId (86-86)
  • agentId (122-122)
  • agentId (160-160)
  • agentId (188-188)
  • agentId (221-221)
  • agentId (258-258)
  • profileOpts (292-295)
  • profileOpts (335-338)
  • disabledConfig (297-297)
  • disabledResult (300-300)
  • disabledResult (342-342)
  • enabledConfig (308-308)
  • currentConfig (355-355)
test/parallel/test-nsolid-heap-profile.js (2)
test/parallel/test-nsolid-heap-profile-stream.js (3)
  • common (4-4)
  • nsolid (6-6)
  • assert (5-5)
lib/nsolid.js (2)
  • err (444-444)
  • err (532-532)
test/agents/test-grpc-profile.mjs (3)
test/agents/test-grpc-heap-profile.mjs (63)
  • grpcServer (88-88)
  • grpcServer (113-113)
  • grpcServer (127-127)
  • grpcServer (154-154)
  • grpcServer (169-169)
  • grpcServer (183-183)
  • grpcServer (197-197)
  • grpcServer (218-218)
  • grpcServer (229-229)
  • grpcServer (252-252)
  • grpcServer (263-263)
  • grpcServer (283-283)
  • grpcServer (302-302)
  • grpcServer (333-333)
  • grpcServer (348-348)
  • grpcServer (377-377)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
  • opts (91-94)
  • opts (130-133)
  • opts (172-175)
  • opts (200-203)
  • opts (232-235)
  • opts (269-272)
  • opts (305-308)
  • opts (351-354)
  • opts (410-413)
  • opts (447-450)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • child (414-414)
  • child (451-451)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
  • profileOpts (311-317)
  • profileOpts (357-363)
  • disabledConfig (319-319)
  • disabledResult (322-322)
  • disabledResult (367-367)
  • enabledConfig (330-330)
  • currentConfig (380-380)
test/agents/test-grpc-heap-sampling.mjs (31)
  • env (79-79)
  • env (118-118)
  • env (159-159)
  • env (187-187)
  • env (219-219)
  • env (254-257)
  • opts (80-83)
  • opts (119-122)
  • opts (160-163)
  • opts (188-191)
  • opts (220-223)
  • opts (258-261)
  • child (84-84)
  • child (123-123)
  • child (164-164)
  • child (192-192)
  • child (225-225)
  • child (262-262)
  • agentId (85-85)
  • agentId (124-124)
  • agentId (165-165)
  • agentId (193-193)
  • agentId (226-226)
  • agentId (263-263)
  • profileOpts (300-306)
  • profileOpts (346-352)
  • disabledConfig (308-308)
  • disabledResult (311-311)
  • disabledResult (356-356)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-grpc-snapshot.mjs (5)
  • disabledConfig (300-300)
  • disabledResult (303-303)
  • disabledResult (345-345)
  • enabledConfig (311-311)
  • currentConfig (358-358)
agents/grpc/src/grpc_agent.cc (1)
agents/zmq/src/zmq_agent.cc (10)
  • config (430-476)
  • config (430-430)
  • config (1263-1378)
  • config (1263-1263)
  • config (2760-2795)
  • config (2760-2760)
  • config (2888-2905)
  • config (2888-2888)
  • config (2983-3000)
  • config (2983-2983)
test/parallel/test-nsolid-heap-sampling-stream.js (4)
test/common/nsolid-grpc-agent/client.js (1)
  • nsolid (7-7)
test/common/nsolid-zmq-agent/client.js (1)
  • nsolid (6-6)
test/parallel/test-nsolid-heap-profile-stream.js (2)
  • nsolid (6-6)
  • common (4-4)
test/parallel/test-nsolid-heap-sampling.js (2)
  • nsolid (5-5)
  • common (3-3)
test/agents/test-grpc-snapshot.mjs (2)
test/agents/test-grpc-heap-profile.mjs (61)
  • grpcServer (88-88)
  • grpcServer (113-113)
  • grpcServer (127-127)
  • grpcServer (154-154)
  • grpcServer (169-169)
  • grpcServer (183-183)
  • grpcServer (197-197)
  • grpcServer (218-218)
  • grpcServer (229-229)
  • grpcServer (252-252)
  • grpcServer (263-263)
  • grpcServer (283-283)
  • grpcServer (302-302)
  • grpcServer (333-333)
  • grpcServer (348-348)
  • grpcServer (377-377)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
  • opts (91-94)
  • opts (130-133)
  • opts (172-175)
  • opts (200-203)
  • opts (232-235)
  • opts (269-272)
  • opts (305-308)
  • opts (351-354)
  • opts (410-413)
  • opts (447-450)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • child (414-414)
  • child (451-451)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
  • disabledConfig (319-319)
  • disabledResult (322-322)
  • disabledResult (367-367)
  • enabledConfig (330-330)
  • currentConfig (380-380)
test/agents/test-grpc-heap-sampling.mjs (29)
  • env (79-79)
  • env (118-118)
  • env (159-159)
  • env (187-187)
  • env (219-219)
  • env (254-257)
  • opts (80-83)
  • opts (119-122)
  • opts (160-163)
  • opts (188-191)
  • opts (220-223)
  • opts (258-261)
  • child (84-84)
  • child (123-123)
  • child (164-164)
  • child (192-192)
  • child (225-225)
  • child (262-262)
  • agentId (85-85)
  • agentId (124-124)
  • agentId (165-165)
  • agentId (193-193)
  • agentId (226-226)
  • agentId (263-263)
  • disabledConfig (308-308)
  • disabledResult (311-311)
  • disabledResult (356-356)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-grpc-heap-profile.mjs (2)
test/agents/test-grpc-heap-sampling.mjs (53)
  • grpcServer (77-77)
  • grpcServer (102-102)
  • grpcServer (116-116)
  • grpcServer (143-143)
  • grpcServer (157-157)
  • grpcServer (171-171)
  • grpcServer (185-185)
  • grpcServer (206-206)
  • grpcServer (217-217)
  • grpcServer (241-241)
  • grpcServer (252-252)
  • grpcServer (272-272)
  • grpcServer (291-291)
  • grpcServer (322-322)
  • grpcServer (337-337)
  • grpcServer (366-366)
  • env (79-79)
  • env (118-118)
  • env (159-159)
  • env (187-187)
  • env (219-219)
  • env (254-257)
  • env (293-293)
  • env (339-339)
  • env (392-392)
  • env (424-424)
  • child (84-84)
  • child (123-123)
  • child (164-164)
  • child (192-192)
  • child (225-225)
  • child (262-262)
  • child (298-298)
  • child (344-344)
  • child (397-397)
  • child (429-429)
  • agentId (85-85)
  • agentId (124-124)
  • agentId (165-165)
  • agentId (193-193)
  • agentId (226-226)
  • agentId (263-263)
  • agentId (299-299)
  • agentId (345-345)
  • agentId (398-398)
  • agentId (438-438)
  • profileOpts (300-306)
  • profileOpts (346-352)
  • disabledConfig (308-308)
  • disabledResult (311-311)
  • disabledResult (356-356)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-grpc-profile.mjs (25)
  • env (80-80)
  • env (116-116)
  • env (154-154)
  • env (182-182)
  • env (214-214)
  • env (249-252)
  • child (85-85)
  • child (121-121)
  • child (159-159)
  • child (187-187)
  • child (220-220)
  • child (257-257)
  • agentId (86-86)
  • agentId (122-122)
  • agentId (160-160)
  • agentId (188-188)
  • agentId (221-221)
  • agentId (258-258)
  • profileOpts (292-295)
  • profileOpts (335-338)
  • disabledConfig (297-297)
  • disabledResult (300-300)
  • disabledResult (342-342)
  • enabledConfig (308-308)
  • currentConfig (355-355)
lib/nsolid.js (1)
agents/grpc/src/grpc_agent.cc (2)
  • config (1000-1186)
  • config (1000-1000)
agents/grpc/src/proto/reconfigure.pb.h (2)
agents/grpc/src/proto/command.pb.h (3)
  • internal (44-47)
  • ReconfigureBody (452-582)
  • ReconfigureBody (837-843)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
test/parallel/test-nsolid-snapshot.js (2)
agents/grpc/src/grpc_errors.h (1)
  • nsolid (16-33)
lib/nsolid.js (2)
  • err (444-444)
  • err (532-532)
agents/grpc/src/proto/reconfigure.pb.cc (1)
agents/grpc/src/proto/reconfigure.pb.h (16)
  • ReconfigureBody (94-94)
  • ReconfigureBody (95-96)
  • ReconfigureBody (681-693)
  • ReconfigureBody (694-698)
  • ReconfigureBody (699-721)
  • ReconfigureBody (722-726)
  • ReconfigureBody (727-749)
  • ReconfigureBody (750-754)
  • ReconfigureBody (755-777)
  • ReconfigureBody (778-782)
  • ReconfigureBody (783-805)
  • ReconfigureBody (806-810)
  • ReconfigureBody (811-834)
  • ReconfigureBody (836-841)
  • ReconfigureBody (875-887)
  • ReconfigureBody (890-903)
test/agents/test-grpc-continuous-profile.mjs (2)
test/agents/test-grpc-heap-profile.mjs (30)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
  • opts (91-94)
  • opts (130-133)
  • opts (172-175)
  • opts (200-203)
  • opts (232-235)
  • opts (269-272)
  • opts (305-308)
  • opts (351-354)
  • opts (410-413)
  • opts (447-450)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
test/common/nsolid-grpc-agent/server.mjs (2)
  • opts (20-27)
  • agentId (67-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build-tarball
  • GitHub Check: lint-js-and-md
  • GitHub Check: test-macOS
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: coverage-windows
🔇 Additional comments (20)
test/agents/test-zmq-heap-sampling.mjs (1)

135-201: LGTM! Comprehensive test coverage for assetsEnabled toggle.

The test thoroughly validates the runtime asset control feature:

  • Properly verifies that heap sampling is rejected with a 422 error when assetsEnabled is false
  • Confirms successful sampling after re-enabling assets
  • Validates profile structure and final configuration state

The resolved flag (lines 142, 170-171) is a good defensive pattern to prevent race conditions from multiple event emissions after test completion.

agents/grpc/src/grpc_errors.h (1)

12-14: New error EAssetsDisabled wired correctly.

Macro continuation and enum expansion look good; this aligns with assets gating behavior and code 1008.

lib/nsolid.js (8)

133-137: Public toggle APIs exposed on start object look good.

enableAssets/disableAssets and enableTraces/disableTraces are clearly surfaced and follow existing conventions.


195-207: Config getters with sensible defaults.

assetsEnabled defaulting to true and tracingEnabled to false matches initializeConfig. Good for pre-start behavior and user ergonomics.


497-499: heapSamplingStream gating matches behavior expectations.

Early throw keeps semantics consistent with argument validation ordering in these APIs.


604-617: enableAssets/disableAssets helpers are idempotent and minimal.

Early returns avoid redundant config churn. Nice.


620-633: enableTraces/disableTraces helpers mirror assets helpers.

Looks consistent and straightforward.


761-766: Config normalization for assetsEnabled is correct.

optionToBool with undefined guard prevents accidental clearing; good precedence handling.


947-957: Env/pkg precedence and default(true) for assetsEnabled are aligned with native.

Good: env > pkg > default(true). Matches expected operator ergonomics.


1106-1116: optionToBool is robust across types.

Covers undefined/null, boolean, number, and string via existing envToBool semantics.

Do we intentionally leave profile(), profileEnd(), and snapshot() ungated at the JS layer, relying solely on native errors? If parity is desired, we can mirror the early-throw pattern there as well. Happy to draft that if needed.

test/parallel/test-nsolid-config-tracing.js (1)

24-29: Runtime tracing helpers keep config in sync — LGTM.

This verifies helper-driven toggles reflect in nsolid.config.tracingEnabled, matching the tracer flag recompute in agents (see GrpcAgent config path).

test/parallel/test-nsolid-snapshot.js (1)

82-108: Good: toggling chained to snapshot completion avoids in‑flight races

Sequencing disableAssets() only after snapshot callback completes is correct and resolves prior race.

test/parallel/test-nsolid-heap-profile.js (1)

133-204: Asset toggle flow is correct and race‑safe

Enable → run → end → disable sequencing with platformTimeouts looks good.

test/parallel/test-nsolid-heap-sampling.js (1)

177-248: Sequencing and assertions look solid

Re-enable, run to completion, then disable with proper waits—LGTM.

test/agents/test-grpc-profile.mjs (2)

243-277: Env‑driven disablement test is clear and correct

Validates NSOLID_ASSETS_ENABLED=0 returns 500 with expected message.


279-320: Config toggle path is well‑covered

Asserts disabled then enabled via config before invoking RPCs—LGTM.

agents/grpc/src/grpc_agent.cc (1)

379-382: LGTM: reconfigure payload round-trips assetsEnabled.

PopulateReconfigureEvent and reconfigure() now propagate assetsEnabled. Matches proto additions.

Also applies to: 1739-1742

test/common/nsolid-grpc-agent/index.js (1)

346-355: config(): good consolidation via #sendAndWait.

API looks fine; returns the mapped config payload.

agents/grpc/src/proto/reconfigure.pb.h (1)

233-235: LGTM: assetsEnabled field (tag 13) added consistently.

Field number, accessors, has-bits mask, and parse table size updated coherently.

Also applies to: 392-402, 408-413, 1175-1202

agents/grpc/src/proto/reconfigure.pb.cc (1)

49-51: LGTM: generated code matches proto changes for assetsEnabled.

Offsets, masks, serialization, merge, and size accounting include field 13 correctly.

Also applies to: 107-121, 339-349, 393-398, 624-661, 702-716

Comment thread agents/grpc/src/grpc_agent.cc
Comment thread agents/zmq/src/zmq_agent.h
Comment thread test/agents/test-zmq-tracing.mjs
Copy link
Copy Markdown

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
configure.py (2)

2322-2324: Consider verifying script existence before execution.

While the bash availability check is good, the code doesn't verify that the apply-patches.sh script actually exists before attempting to run it. If the script is missing, subprocess.run will fail with a potentially unclear error message.

Consider this improvement:

 # Check if we can run bash scripts (available on Unix-like systems and Windows with git/bash)
 bash_available = shutil.which('bash') is not None
-if bash_available:
+if bash_available and script.is_file():
   subprocess.run(['bash', str(script)], check=True)
+elif bash_available:
+  warn(f'V8 patch script not found: {script}')
 else:
   warn('bash not found - skipping V8 patches. Make sure git bash or similar is available on Windows.')

2325-2326: Warning message could be more informative about consequences.

The warning informs users that bash is missing but doesn't explain the potential impact of skipping V8 patches. Consider making it clearer what functionality might be affected.

For example:

 else:
-  warn('bash not found - skipping V8 patches. Make sure git bash or similar is available on Windows.')
+  warn('bash not found - skipping V8 patches. V8 may not function correctly without patches. On Windows, install Git Bash or WSL to apply patches.')
agents/grpc/src/grpc_utils.h (4)

4-6: Add missing standard headers for used symbols.

std::string and std::snprintf are used but <string> and <cstdio> are not included here. Avoid relying on transitive includes from other headers.

 #include "debug_utils-inl.h"
 #include "google/protobuf/util/json_util.h"
 #include "nlohmann/json.hpp"
+#include <cstdio>
+#include <string>

8-10: Avoid global using in headers; fully qualify protobuf types/functions.

Placing using google::protobuf::Message and using google::protobuf::util::MessageToJsonString at global scope in a header pollutes all including translation units and risks symbol collisions. Prefer fully qualified names locally.

Remove the global using-declarations:

-using google::protobuf::Message;
-using google::protobuf::util::MessageToJsonString;

Fully qualify in the API and call site:

-inline void DebugProtobufMsg(const char* format,
-                             const Message& msg,
+inline void DebugProtobufMsg(const char* format,
+                             const google::protobuf::Message& msg,
                              Args&&... args) {
   if (per_process::enabled_debug_list.enabled(
         DebugCategory::NSOLID_GRPC_AGENT)) {
     std::string json_payload;
-    const auto status = MessageToJsonString(msg, &json_payload);
+    const auto status =
+        google::protobuf::util::MessageToJsonString(msg, &json_payload);

Also applies to: 29-36


21-26: Stabilize JSON string lifetime before logging.

Store msg.dump(4) in a local std::string before passing .c_str() to the logger to avoid any surprises if the logger defers formatting.

 inline void DebugJSON(const char* str, const nlohmann::json& msg) {
   if (per_process::enabled_debug_list.enabled(
         DebugCategory::NSOLID_GRPC_AGENT)) {
-    Debug(str, msg.dump(4).c_str());
+    const std::string dumped = msg.dump(4);
+    Debug(str, dumped.c_str());
   }
 }

35-49: Harden formatting and error path; handle truncation and null format.

  • snprintf result is unchecked; long prefixes silently truncate to 512 bytes.
  • Assigning format directly to std::string when no args assumes format != nullptr.
  • On protobuf->JSON failure, nothing is logged; add a clear failure log.
     std::string json_payload;
-    const auto status = MessageToJsonString(msg, &json_payload);
+    const auto status =
+        google::protobuf::util::MessageToJsonString(msg, &json_payload);
     if (status.ok()) {
       // Build complete message with format args + JSON
       std::string formatted_prefix;
       if constexpr (sizeof...(args) > 0) {
-        char buffer[512];
-        snprintf(buffer, sizeof(buffer), format, std::forward<Args>(args)...);
-        formatted_prefix = buffer;
+        char buffer[512];
+        int n = std::snprintf(buffer,
+                              sizeof(buffer),
+                              format ? format : "",
+                              std::forward<Args>(args)...);
+        if (n < 0) {
+          formatted_prefix.clear();
+        } else if (static_cast<size_t>(n) >= sizeof(buffer)) {
+          buffer[sizeof(buffer) - 1] = '\0';
+          formatted_prefix.assign(buffer);
+          formatted_prefix += "… (truncated)";
+        } else {
+          formatted_prefix.assign(buffer);
+        }
       } else {
-        formatted_prefix = format;
+        formatted_prefix = (format != nullptr) ? format : "";
       }
       Debug("%s%s\n", formatted_prefix.c_str(), json_payload.c_str());
       return;
-    }
+    } else {
+      const std::string status_msg = status.ToString();
+      Debug("Protobuf->JSON conversion failed for '%s': %s\n",
+            (format ? format : ""),
+            status_msg.c_str());
+    }

If the project already uses a safer formatter (e.g., {fmt}), I can switch this to fmt::format for type‑safe, dynamic sizing. Confirm preference.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4fdf79 and 3213e9d.

📒 Files selected for processing (2)
  • agents/grpc/src/grpc_utils.h (1 hunks)
  • configure.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
agents/grpc/src/grpc_utils.h (1)
src/debug_utils.h (1)
  • DebugCategory (67-71)
🪛 Ruff (0.14.1)
configure.py

2324-2324: subprocess call: check for execution of untrusted input

(S603)


2324-2324: Starting a process with a partial executable path

(S607)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-macOS
  • GitHub Check: build-tarball
  • GitHub Check: lint-js-and-md
  • GitHub Check: coverage-windows
🔇 Additional comments (2)
agents/grpc/src/grpc_utils.h (2)

15-19: Nice wrapper; forwards category consistently.

The variadic Debug helper cleanly scopes logs to NSOLID_GRPC_AGENT. No issues spotted.


28-33: Build standard check — OK (C++17+ enforced).

agents/grpc sources are built from node.gyp (agents/grpc/* listed there) and the project gyp includes common_node.gypi which sets cflags_cc / CLANG_CXX_LANGUAGE_STANDARD to gnu++20, so this TU will be compiled with C++20 (>= C++17).
(node.gyp; common_node.gypi).

Copy link
Copy Markdown

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
configure.py (1)

2321-2333: LGTM! Defensive approach improves robustness.

The availability checks for bash and git before running the V8 patches script are a good defensive programming practice, allowing the configure process to gracefully handle environments where these tools are missing.

Optional improvement: Consider using the full paths returned by shutil.which() in the subprocess call for consistency and slightly better security posture:

 bash_available = shutil.which('bash') is not None
 git_available = shutil.which('git') is not None
 
 if bash_available and git_available:
-  subprocess.run(['bash', str(script)], check=True)
+  bash_path = shutil.which('bash')
+  subprocess.run([bash_path, str(script)], check=True)
 else:

Note: The static analysis warnings (S603, S607) can be safely ignored here—the script path is from a known repository location, and using 'bash' from PATH is standard practice for configuration scripts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3213e9d and 545b1e6.

📒 Files selected for processing (1)
  • configure.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.14.1)
configure.py

2326-2326: subprocess call: check for execution of untrusted input

(S603)


2326-2326: Starting a process with a partial executable path

(S607)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: build-tarball
  • GitHub Check: test-macOS
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md

Copy link
Copy Markdown

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0000b78 and 9facded.

📒 Files selected for processing (2)
  • test/agents/test-grpc-blocked-loop.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-macOS
  • GitHub Check: lint-js-and-md
  • GitHub Check: coverage-windows
  • GitHub Check: build-tarball
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
🔇 Additional comments (3)
test/common/nsolid-grpc-agent/index.js (2)

346-387: New API methods look good.

The new methods (config, enableAssets, disableAssets, enableTraces, disableTraces, trace) follow consistent patterns and properly check for the child process before sending messages. The refactoring of config to use #sendAndWait improves code maintainability.

Note: The trace() method correctly uses a different pattern than the enable/disable methods because it waits for send completion rather than a response message.


18-18: The validation change for requestId is intentional and correct.

Based on verification of the codebase, this change reflects proper request tracking. The test file generates requestIds as UUIDs (using randomUUID()) and uses them to match responses. Validating that requestId is a string (rather than asserting it must be empty) aligns with actual usage where requestIds contain unique identifiers for tracking operations like profiling, snapshots, and metrics collection.

test/agents/test-grpc-blocked-loop.mjs (1)

72-72: Verify the validation relaxation for requestId is consistent with expected behavior.

Both checkBlockedLoopData and checkUnblockedLoopData now validate requestId as any string instead of asserting it equals an empty string. This matches the change in test/common/nsolid-grpc-agent/index.js (line 18).

Please confirm this is the intended behavior across all command types (exit, loop_blocked, loop_unblocked). If these commands should still have empty requestIds, the validation has been weakened.

Also applies to: 122-122

Comment thread test/common/nsolid-grpc-agent/index.js
@santigimeno santigimeno force-pushed the node-v22.x-nsolid-v6.x branch from d359fc9 to d08dcb2 Compare October 28, 2025 20:19
@santigimeno santigimeno changed the base branch from node-v22.x-nsolid-v6.x to node-v24.x-nsolid-v6.x November 19, 2025 14:37
Copy link
Copy Markdown
Member

@RafaelGSS RafaelGSS left a comment

Choose a reason for hiding this comment

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

RSLGTM

@santigimeno santigimeno force-pushed the santi/assets_enabled branch 2 times, most recently from 7c6a746 to 97e5eea Compare December 2, 2025 09:46
Copy link
Copy Markdown

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
agents/zmq/src/zmq_agent.h (1)

731-731: Initialize assets_enabled_ to avoid undefined behavior.

This atomic boolean is declared without initialization, leaving it in an indeterminate state until config explicitly sets it. If the config lacks an "assetsEnabled" key, reading this variable results in undefined behavior.

Per the previous review, initialize to true to match the JS default:

-  std::atomic<bool> assets_enabled_;
+  std::atomic<bool> assets_enabled_{true};
🧹 Nitpick comments (2)
test/parallel/test-nsolid-heap-profile-stream.js (1)

118-120: Remove redundant assertion.

assert.ok(true) is a no-op. The common.mustCall() wrapper already verifies the callback executes.

           finalStream.on('end', common.mustCall(() => {
-            assert.ok(true);
           }));
lib/nsolid.js (1)

416-419: Remove unused options parameter from heapProfileStream function.

The options parameter is declared in the function signature but never used in the function body. It's not validated, passed to any function calls, or referenced anywhere. Remove it unless it's explicitly intended for future use.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0340d22 and 97e5eea.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (3 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • test/common/nsolid-zmq-agent/client.js
  • test/common/nsolid-grpc-agent/client.js
  • test/parallel/test-nsolid-config-tracing.js
  • src/nsolid/nsolid_api.cc
  • test/agents/test-zmq-tracing.mjs
  • agents/zmq/src/zmq_agent.cc
  • test/parallel/test-nsolid-heap-sampling-stream.js
  • test/common/nsolid-zmq-agent/index.js
  • test/agents/test-grpc-heap-sampling.mjs
  • test/agents/test-grpc-profile.mjs
  • test/common/nsolid-grpc-agent/index.js
  • test/agents/test-zmq-heap-sampling.mjs
  • agents/grpc/src/grpc_agent.h
  • agents/grpc/proto/reconfigure.proto
  • test/agents/test-zmq-profile.mjs
  • agents/grpc/src/grpc_errors.h
  • agents/grpc/src/grpc_agent.cc
  • agents/grpc/src/proto/reconfigure.pb.h
🧰 Additional context used
🧬 Code graph analysis (8)
test/agents/test-grpc-snapshot.mjs (2)
test/agents/test-grpc-heap-sampling.mjs (5)
  • disabledConfig (308-308)
  • disabledResult (311-311)
  • disabledResult (356-356)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-grpc-profile.mjs (5)
  • disabledConfig (297-297)
  • disabledResult (300-300)
  • disabledResult (342-342)
  • enabledConfig (308-308)
  • currentConfig (355-355)
test/parallel/test-nsolid-heap-sampling.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/parallel/test-nsolid-heap-profile.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/agents/test-grpc-continuous-profile.mjs (2)
test/agents/test-grpc-heap-profile.mjs (22)
  • tests (81-81)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • child (414-414)
  • child (451-451)
  • currentConfig (380-380)
test/agents/test-grpc-profile.mjs (2)
  • tests (72-72)
  • currentConfig (355-355)
agents/grpc/src/proto/reconfigure.pb.cc (1)
agents/grpc/src/proto/reconfigure.pb.h (16)
  • ReconfigureBody (94-94)
  • ReconfigureBody (95-96)
  • ReconfigureBody (681-693)
  • ReconfigureBody (694-698)
  • ReconfigureBody (699-721)
  • ReconfigureBody (722-726)
  • ReconfigureBody (727-749)
  • ReconfigureBody (750-754)
  • ReconfigureBody (755-777)
  • ReconfigureBody (778-782)
  • ReconfigureBody (783-805)
  • ReconfigureBody (806-810)
  • ReconfigureBody (811-834)
  • ReconfigureBody (836-841)
  • ReconfigureBody (875-887)
  • ReconfigureBody (890-903)
test/agents/test-grpc-tracing.mjs (1)
test/agents/test-zmq-tracing.mjs (1)
  • phase (411-411)
test/agents/test-grpc-heap-profile.mjs (2)
test/agents/test-grpc-profile.mjs (5)
  • disabledConfig (297-297)
  • disabledResult (300-300)
  • disabledResult (342-342)
  • enabledConfig (308-308)
  • currentConfig (355-355)
test/agents/test-grpc-snapshot.mjs (5)
  • disabledConfig (300-300)
  • disabledResult (303-303)
  • disabledResult (345-345)
  • enabledConfig (311-311)
  • currentConfig (358-358)
test/agents/test-zmq-heap-profile.mjs (1)
test/agents/test-grpc-heap-profile.mjs (3)
  • disabledConfig (319-319)
  • enabledConfig (330-330)
  • currentConfig (380-380)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-tarball
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: coverage-linux
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md
🔇 Additional comments (35)
agents/grpc/src/proto/reconfigure.pb.cc (1)

1-4: Auto-generated protobuf code - no manual edits required.

This file is generated by the protocol buffer compiler from reconfigure.proto. The changes correctly add the new assetsEnabled field (tag 13) with proper has-bit handling, serialization, parsing, merge, and swap logic consistent with other boolean fields in ReconfigureBody.

test/agents/test-zmq-heap-profile.mjs (1)

342-408: Well-structured test for assetsEnabled toggle flow.

The test correctly validates the full enable/disable cycle: disabling assets returns a 422 error for heap profile requests, re-enabling allows successful profiling, and the final state is verified. The use of the resolved flag prevents duplicate event handling, and the async/await pattern is properly applied.

test/agents/test-zmq-snapshot.mjs (1)

235-296: Consistent test pattern for assetsEnabled toggle on snapshots.

The test follows the established pattern from heap profile tests, properly validating the 422 error when assets are disabled, successful snapshot capture when enabled, and final state verification. The snapshot data accumulation and JSON parsing validation are correctly implemented.

test/parallel/test-nsolid-snapshot.js (1)

60-110: Correctly implements asset toggle tests with proper sequencing.

The implementation properly addresses the race condition concern from the previous review by using common.mustSucceed() callbacks to wait for snapshot completion before toggling the assets state. The nested setImmediate calls ensure proper ordering of operations.

test/parallel/test-nsolid-heap-sampling.js (2)

174-175: Good use of platformTimeout for cross-platform compatibility.

Replacing hardcoded timeouts with common.platformTimeout(100) ensures tests work reliably across different platforms with varying performance characteristics.


177-248: Proper sequencing of asset toggle tests for heap sampling.

The test correctly validates the full asset enable/disable cycle for heap sampling operations. The implementation properly waits for sampling operations to complete via common.mustSucceed() callbacks before toggling asset state, avoiding race conditions. The consistent use of common.platformTimeout(100) ensures cross-platform reliability.

test/parallel/test-nsolid-heap-profile-stream.js (2)

68-69: LGTM!

Clean integration point - calling the new asset toggle tests after the existing test completes successfully.


72-126: LGTM! Comprehensive asset toggle test coverage.

The test correctly exercises multiple toggle paths:

  1. Disabling via nsolid.start({ assetsEnabled: false })
  2. Re-enabling via enableAssets() helper
  3. Disabling via disableAssets() helper
  4. Final re-enable to confirm restoration

The nested setImmediate pattern is appropriate for ensuring state propagation before assertions.

test/parallel/test-nsolid-profile.js (2)

107-118: LGTM!

Good changes:

  • Reformatted assertion for readability
  • Added integration point for asset toggle tests
  • Using common.platformTimeout(100) ensures cross-platform reliability

120-182: LGTM! Well-structured asset toggle tests for CPU profiling.

The test correctly:

  1. Disables assets and verifies both synchronous throw and callback error paths for profile() and profileEnd()
  2. Re-enables assets and completes a full profile cycle before toggling again
  3. Uses appropriate timeouts between state changes
test/agents/test-grpc-continuous-profile.mjs (7)

4-4: LGTM!

Adding setTimeout from node:timers/promises enables cleaner async/await timing in tests.


121-156: LGTM!

The test correctly verifies that when NSOLID_ASSETS_ENABLED='0' is set via environment, no continuous profiles are emitted. The 300ms wait appropriately covers multiple profile intervals.


158-201: LGTM!

Well-designed test that:

  1. Verifies continuous profiling works initially
  2. Confirms profiles stop (or nearly stop) after disabling assets via config
  3. Validates that re-enabling assets resumes profiling

The <= 1 tolerance at line 189 correctly accounts for an in-flight profile that may complete after the disable call.


203-245: LGTM!

This test complements the previous one by exercising the enableAssets()/disableAssets() helper methods. The additional config verification at lines 236-237 is a nice touch.


255-266: LGTM!

Refactored setup for consistency with other tests. Moving env, opts, and startTime initialization before grpcServer.on('profile', ...) improves code organization.


380-386: LGTM!

Clean refactor from callback-style setTimeout to await setTimeout() from node:timers/promises.


430-449: LGTM!

Same async/await refactor pattern applied consistently.

test/agents/test-grpc-snapshot.mjs (3)

246-280: LGTM!

The test correctly verifies that when NSOLID_ASSETS_ENABLED='0' is set via environment, heap snapshot requests return a 500 error with the appropriate "Assets collection disabled(1008)" message.


282-323: LGTM!

Well-structured test that:

  1. Disables assets and verifies the config change took effect
  2. Confirms snapshot request fails with the expected error
  3. Re-enables assets and verifies snapshot succeeds

325-367: LGTM!

This test exercises the enableAssets()/disableAssets() helper methods for snapshot operations, complementing the config-based toggle test above.

test/parallel/test-nsolid-heap-profile.js (2)

124-131: LGTM!

Clean integration point calling the new asset toggle tests. Using common.platformTimeout(100) ensures cross-platform reliability.


133-204: LGTM! Thorough asset toggle coverage for heap profiling.

The test correctly exercises the same toggle scenarios as the CPU profile test but for the heap profiling API. While there's structural similarity with test-nsolid-profile.js, the tests target different APIs and keeping them separate maintains clarity.

test/agents/test-grpc-heap-profile.mjs (3)

259-296: LGTM: Well-structured test for environment-based asset disabling.

The test correctly sets NSOLID_ASSETS_ENABLED: '0' and verifies the expected 500 error response with the 'Assets collection disabled(1008)' message. The pattern is consistent with other error-handling tests in this file.


298-342: LGTM: Comprehensive test for config-based asset toggling.

Good coverage of the toggle flow: disabling via config({ assetsEnabled: false }), verifying the error, re-enabling via config({ assetsEnabled: true }), and confirming successful profiling. Consistent with the pattern shown in the relevant snippets from test-grpc-profile.mjs and test-grpc-snapshot.mjs.


344-389: LGTM: Good coverage for helper function API.

The test exercises disableAssets() and enableAssets() helpers and includes a final verification that currentConfig.assetsEnabled is true after re-enabling, which provides additional confidence in the API consistency.

lib/nsolid.js (7)

133-136: LGTM: Clean API surface additions.

The new toggle functions are properly exposed alongside the existing profiling APIs.


201-214: LGTM: Sensible defaults for new getters.

Using ?? true for assetsEnabled (enabled by default) and ?? false for tracingEnabled (disabled by default) aligns with the initialization logic in initializeConfig.


502-504: LGTM: Consistent assets check for heap sampling.

The guard follows the same pattern as heapProfileStream, throwing the appropriate error type when assets are disabled.


609-638: LGTM: Simple and idempotent toggle implementations.

The early return when already in the desired state prevents unnecessary config updates. All four functions follow the same consistent pattern.


766-770: LGTM: Proper normalization in updateConfig.

Using optionToBool ensures consistent handling of various input types, and only updating when normalized !== undefined preserves existing behavior when invalid input is provided.


952-961: LGTM: Correct precedence for assetsEnabled initialization.

The env → pkg → default (true) precedence is properly implemented and consistent with other configuration options in this function.


1111-1121: LGTM: Well-designed type coercion utility.

optionToBool correctly handles all expected input types (undefined, null, boolean, number, string) and returns undefined for invalid types, allowing callers to distinguish between "not set" and "set to false".

test/agents/test-grpc-tracing.mjs (3)

5-5: LGTM: Import addition is appropriate.

The setTimeout as delay import is correctly added and used later in the test for timing control between phase transitions.


205-247: LGTM: Phase-based testing approach is well-structured.

The phase state machine (initialdisabledreenableddone) effectively validates the enable/disable traces functionality:

  1. Early return guard (lines 207-209) prevents post-completion processing
  2. Array reset via resourceSpans.length = 0 ensures clean state between phases
  3. 200ms delay provides sufficient time to verify spans are not emitted when disabled
  4. mustCallAtLeast(2) correctly reflects listener calls in both initial and reenabled phases
  5. child.shutdown(0) is appropriately deferred to the final phase

The test comprehensively verifies that traces are collected, not collected when disabled, and collected again when re-enabled.


273-315: LGTM: Custom tracing test mirrors HTTP tracing approach.

The custom tracing test correctly implements the same phase-based enable/disable verification as the HTTP tracing test, ensuring consistent coverage across different tracing types. The implementation maintains the same defensive patterns and timing controls.

Comment thread lib/nsolid.js
Copy link
Copy Markdown

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
test/agents/test-grpc-tracing.mjs (1)

107-135: Fix checkHttpSpans to validate the client span IDs, not the server span twice

checkHttpSpans currently calls validateId(serverSpan.traceId/spanId, ...) again when checking the client span (Lines 125–127), so the client span’s IDs are never actually validated. This is pre‑existing but worth correcting:

  const clientSpan = spans[1];
- validateId(serverSpan.traceId, 16);
- validateId(serverSpan.spanId, 8);
+ validateId(clientSpan.traceId, 16);
+ validateId(clientSpan.spanId, 8);
agents/grpc/src/grpc_agent.cc (1)

2076-2097: Fix data race on config_ when accessed from JS threads in do_start_prof_init()

do_start_prof_init() is called from both GRPC handlers (start_*_profile variants) running on the agent loop thread and from *_from_js entry points that execute on arbitrary JS/V8 threads. The method reads config_ at lines 2092-2093:

auto it = config_.find("contCpuProfile");
if (it != config_.end() && it->get<bool>() == true) {
  return ErrorType::EInProgressError;
}

While assets_enabled_ is atomic and thread-safe, config_ is a plain nlohmann::json member with no synchronization mechanism. Although writes to config_ only occur on the agent loop thread via config_msg_cb_(), reads from JS threads create a data race and undefined behavior per C++11 memory model.

To eliminate the race, snapshot the continuous-profile flag into an atomic variable:

// grpc_agent.h
std::atomic<bool> cont_cpu_profile_enabled_{false};
// In config() method after updating config_:
{
  auto it = config_.find("contCpuProfile");
  if (it != config_.end() && it->is_boolean()) {
    cont_cpu_profile_enabled_.store(*it, std::memory_order_relaxed);
  } else {
    cont_cpu_profile_enabled_.store(false, std::memory_order_relaxed);
  }
}
// In do_start_prof_init():
if (!assets_enabled_.load(std::memory_order_acquire)) {
  return ErrorType::EAssetsDisabled;
}
if (type == ProfileType::kCpu &&
    cont_cpu_profile_enabled_.load(std::memory_order_acquire)) {
  return ErrorType::EInProgressError;
}

This keeps all config_ access confined to the agent thread and safely communicates the continuous-profile state to off-thread callers.

♻️ Duplicate comments (1)
test/common/nsolid-grpc-agent/index.js (1)

389-406: Tighten #sendAndWait timeout behavior and avoid payload overriding type

Two follow-ups here:

  1. Timeout mapping can throw for mapped calls (e.g. config())
    On timeout you call resolve(map ? map(null) : undefined). For config(), map is msg => msg.config, so a timeout will attempt null.config and throw. Prefer to resolve a sentinel (e.g. null) without invoking map on timeout:

    const timeout = setTimeout(() => {
  • this.#child.off('message', handler);
  • resolve(map ? map(null) : undefined);
  • this.#child.off('message', handler);
  • resolve(undefined); // or resolve(null), leaving mapping to the caller
    }, 30000);

Callers that care can wrap `config()` and handle `undefined`/`null` explicitly.

2. **Payload can still override `type`** *(previously flagged)*  
The object sent is `{ type, ...payload }`, so a `payload.type` field would override the intended message type if introduced later. To make this robust, flip the spread order:

```diff
-      this.#child.on('message', handler);
-      this.#child.send({ type, ...payload });
+      this.#child.on('message', handler);
+      this.#child.send({ ...payload, type });

That keeps the explicit type authoritative.

🧹 Nitpick comments (7)
test/parallel/test-nsolid-heap-profile-stream.js (1)

80-125: Optional: consider flattening the nested async/control-flow for readability.

The nested setImmediate and stream callbacks are functionally fine but a bit hard to scan; if you touch this again, consider extracting small helpers (e.g., expectHeapStreamFailsWhenDisabled, collectHeapProfile, withAssetsEnabled) or using a simple step-wise driver to reduce indentation while keeping the same assertions.

agents/zmq/src/zmq_agent.cc (1)

2116-2118: Consider a more descriptive error message for disabled assets.

The guard correctly prevents profiling when assets are disabled. However, returning UV_EINVAL maps to the generic error message "Invalid arguments" (line 1546-1548), which doesn't clearly indicate that assets are disabled.

Consider adding a specific error code for disabled assets to provide clearer feedback to users. This would help distinguish between "assets disabled" and actual invalid arguments:

 int ZmqAgent::do_start_prof_init(const nlohmann::json& message,
                                  ProfileType type,
                                  ProfileOptions& options) {
   if (!assets_enabled_) {
-    return UV_EINVAL;
+    return UV_EPERM;  // Or introduce a custom error code
   }

Then update create_command_error() to handle this case:

 ZmqAgent::ZmqCommandError ZmqAgent::create_command_error(
     const std::string& command, int err) const {
   ZmqCommandError cmd_error;
   switch (err) {
+    case UV_EPERM:
+      cmd_error.code = 403;
+      cmd_error.message = "Asset collection is disabled";
+    break;
     case UV_EEXIST:
       cmd_error.code = 409;

Alternatively, keep the current approach if consistency with existing error patterns is preferred.

test/agents/test-zmq-profile.mjs (1)

149-173: Avoid async event handler to prevent silent promise rejections

The event callback passed to playground.bootstrap is async and awaits playground.client.config() after the asset stream completes. If config() rejects or the assertions after the await fail, the resulting rejected promise may not be observed by the bootstrap/event machinery, depending on how it wires listeners, leading to an unhandled rejection rather than a clean test failure.

You can keep the handler synchronous and move the await work into a fire-and-forget IIFE so assertion failures still throw synchronously before the first await, while post-await failures at least surface as unhandled rejections you can manage at the harness level:

-      }), async (eventType, agentId, data) => {
+      }), (eventType, agentId, data) => {
         if (resolved)
           return;
@@
-          case 2: {
+          case 2: {
             assert.strictEqual(eventType, 'asset-received');
             checkProfileData(requestId, options, agentId, data, true);
             JSON.parse(profile);
             resolved = true;
-            const currentConfig = await playground.client.config();
-            assert.strictEqual(currentConfig.assetsEnabled, true);
-            resolve();
+            (async () => {
+              const currentConfig = await playground.client.config();
+              assert.strictEqual(currentConfig.assetsEnabled, true);
+              resolve();
+            })();
           }

This keeps the external handler signature non-async while preserving the behavior. Please double-check how playground.bootstrap treats async listeners; if it already awaits or handles returned promises, you may choose to keep the current form.

test/parallel/test-nsolid-heap-sampling.js (1)

177-248: Good coverage of assetsEnabled toggling; consider tightening assertions and timers (optional)

The new runHeapSamplingAssetsToggleTests() function does a nice job exercising:

  • disabling assets via nsolid.start({ assetsEnabled: false }),
  • validating both sync (assert.throws) and callback error paths for heapSampling/heapSamplingEnd,
  • re-enabling via enableAssets(), running a full sampling cycle, then disabling again with disableAssets(),
  • and finally re-enabling to confirm sampling still works.

Two optional improvements you might consider:

  1. Assert on an assets-specific error code if available
    Right now the tests only assert on the generic messages 'Heap sampling could not be started' / 'stopped' plus err.code !== 0. If there is a stable error code that uniquely identifies the “assets disabled” condition, checking that here would make the tests more targeted and avoid passing when some unrelated failure produces the same message.

  2. Wrap timers with common.mustCall for stricter async guarantees
    Using setTimeout(common.mustCall(...), common.platformTimeout(100)) for the new timers would surface clearer failures if a path is never reached (e.g., due to earlier unexpected errors), and is consistent with how many other async tests enforce callback execution.

Both are nice-to-have and can be deferred if you prefer to keep the test light.

test/parallel/test-nsolid-profile.js (1)

120-182: LGTM! Comprehensive test for assets toggle lifecycle.

The runAssetsToggleTests() function thoroughly tests:

  1. Assets disabled via config → profile fails
  2. Assets enabled via enableAssets() → profile succeeds
  3. Assets disabled via disableAssets() → profile fails
  4. Assets re-enabled → profile succeeds

The deeply nested callbacks (6+ levels) could be flattened using async/await for improved readability:

-function runAssetsToggleTests() {
-  // Disable assets through config update
-  nsolid.start({
-    command: 'localhost:9001',
-    data: 'localhost:9002',
-    assetsEnabled: false,
-  });
-
-  setTimeout(() => {
-    // ... deeply nested callbacks
-  }, common.platformTimeout(100));
-}
+async function runAssetsToggleTests() {
+  const { promisify } = require('util');
+  const delay = promisify(setTimeout);
+  
+  nsolid.start({
+    command: 'localhost:9001',
+    data: 'localhost:9002',
+    assetsEnabled: false,
+  });
+  
+  await delay(common.platformTimeout(100));
+  // ... flattened async logic
+}
test/agents/test-zmq-heap-profile.mjs (1)

365-370: Consider aligning ZMQ error code for disabled assets with gRPC's more specific error.

The ZMQ agent returns error code 422 "Invalid arguments" when assets are disabled, while the gRPC agent returns 500 "Assets collection disabled(1008)". This occurs because ZMQ maps the assets_enabled_ check in do_start_prof_init() to UV_EINVAL, which translates to code 422. Consider whether the ZMQ agent should return a dedicated error code (like 500) with a more descriptive message to match gRPC's approach and provide clearer API semantics.

lib/nsolid.js (1)

715-772: Bool normalization for assetsEnabled via optionToBool and env/pkg precedence looks solid

  • updateConfig() now normalizes assetsEnabled from the JS config using optionToBool, only applying it when the value is interpretable as a boolean.
  • initializeConfig() derives nsolidConfig.assetsEnabled from:
    1. NSOLID_ASSETS_ENABLED env (via optionToBool),
    2. package.json’s nsolid.assetsEnabled (also via optionToBool),
    3. falling back to true if both are absent.
  • optionToBool() sensibly handles booleans, numbers, and strings (using the existing envToBool semantics), returning undefined for unsupported types so callers can skip invalid values.

This yields predictable precedence and avoids surprising behavior from odd config types.

Also applies to: 951-960, 1110-1120

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97e5eea and c1da77d.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (3 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • test/parallel/test-nsolid-heap-sampling-stream.js
  • test/common/nsolid-zmq-agent/index.js
  • test/parallel/test-nsolid-snapshot.js
  • test/common/nsolid-grpc-agent/client.js
  • agents/zmq/src/zmq_agent.h
  • test/parallel/test-nsolid-config-tracing.js
  • test/agents/test-zmq-heap-sampling.mjs
  • agents/grpc/proto/reconfigure.proto
  • src/nsolid/nsolid_api.cc
  • test/agents/test-grpc-profile.mjs
🧰 Additional context used
🧬 Code graph analysis (11)
test/parallel/test-nsolid-heap-profile-stream.js (4)
test/common/nsolid-grpc-agent/client.js (1)
  • nsolid (7-7)
test/common/nsolid-zmq-agent/client.js (1)
  • nsolid (6-6)
test/parallel/test-nsolid-config-tracing.js (2)
  • nsolid (5-5)
  • assert (4-4)
test/parallel/test-nsolid-heap-profile.js (3)
  • nsolid (5-5)
  • assert (4-4)
  • common (3-3)
test/agents/test-zmq-tracing.mjs (1)
lib/internal/process/pre_execution.js (1)
  • threadId (726-726)
test/agents/test-grpc-snapshot.mjs (2)
test/agents/test-grpc-heap-sampling.mjs (3)
  • disabledConfig (308-308)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-grpc-profile.mjs (3)
  • disabledConfig (297-297)
  • enabledConfig (308-308)
  • currentConfig (355-355)
test/agents/test-grpc-continuous-profile.mjs (1)
test/agents/test-grpc-heap-profile.mjs (10)
  • env (90-90)
  • env (129-129)
  • env (171-171)
  • env (199-199)
  • env (231-231)
  • env (265-268)
  • env (304-304)
  • env (350-350)
  • env (408-408)
  • env (446-446)
test/agents/test-zmq-heap-profile.mjs (1)
test/agents/test-zmq-heap-sampling.mjs (4)
  • playground (512-512)
  • disabledConfig (154-154)
  • enabledConfig (165-165)
  • currentConfig (193-193)
test/agents/test-zmq-profile.mjs (1)
test/agents/test-zmq-heap-profile.mjs (10)
  • playground (491-491)
  • profile (43-43)
  • profile (98-98)
  • profile (252-252)
  • profile (298-298)
  • profile (347-347)
  • profile (415-415)
  • disabledConfig (362-362)
  • enabledConfig (373-373)
  • currentConfig (400-400)
test/parallel/test-nsolid-heap-profile.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
agents/grpc/src/proto/reconfigure.pb.h (1)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
agents/grpc/src/grpc_agent.cc (1)
agents/zmq/src/zmq_agent.cc (10)
  • config (430-476)
  • config (430-430)
  • config (1264-1379)
  • config (1264-1264)
  • config (2761-2796)
  • config (2761-2761)
  • config (2889-2906)
  • config (2889-2889)
  • config (2984-3001)
  • config (2984-2984)
test/common/nsolid-grpc-agent/index.js (1)
test/common/nsolid-grpc-agent/client.js (1)
  • msg (89-89)
test/agents/test-grpc-heap-sampling.mjs (1)
test/agents/test-grpc-heap-profile.mjs (33)
  • tests (81-81)
  • grpcServer (88-88)
  • grpcServer (113-113)
  • grpcServer (127-127)
  • grpcServer (154-154)
  • grpcServer (169-169)
  • grpcServer (183-183)
  • grpcServer (197-197)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
  • profileOpts (311-317)
  • profileOpts (357-363)
  • disabledConfig (319-319)
  • disabledResult (322-322)
  • disabledResult (367-367)
  • enabledConfig (330-330)
  • currentConfig (380-380)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: lint-js-and-md
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-windows
  • GitHub Check: coverage-linux
🔇 Additional comments (29)
test/parallel/test-nsolid-heap-profile-stream.js (1)

68-69: Assets toggle stream test flow and sync-throw expectations look correct.

The added runHeapProfileStreamAssetsToggleTests() sequence (disable via start → assert sync failure → enable and validate successful stream → disable and assert sync failure again → final enable + successful stream) exercises the intended runtime assets control thoroughly, and using assert.throws for the disabled-assets cases aligns with the synchronous validation behavior of heapProfileStream() (immediate state checks vs. async runtime errors). Based on learnings, this matches the established error-handling contract for this API.

Also applies to: 72-126

agents/zmq/src/zmq_agent.cc (2)

662-662: LGTM!

Initialization of assets_enabled_ to true preserves backward compatibility by keeping asset collection enabled by default.


1283-1288: Verify whether assetsEnabled reconfiguration is intentionally ZMQ-only limited.

The implementation correctly reads assetsEnabled from the configuration (lines 1283-1288). However, assetsEnabled is excluded from the ZMQ agent's validate_reconfigure() allowed options list (lines 2495-2507), meaning it cannot be changed via the ZMQ reconfigure command at runtime.

In contrast, the gRPC agent includes assetsEnabled as an optional boolean field in its reconfigure protobuf definition (agents/grpc/proto/reconfigure.proto, line 20) and handles it in the reconfigure response (grpc_agent.cc, line 1740).

Clarify whether this difference is intentional—that is, whether runtime reconfiguration of assetsEnabled is only supported via the gRPC protocol and explicitly excluded from ZMQ, or if this represents a gap that should be addressed.

test/agents/test-zmq-snapshot.mjs (1)

235-296: New assetsEnabled toggle snapshot test looks correct and consistent

This test cleanly exercises the “disabled → 422 error → re‑enable → successful snapshot” flow, reuses the existing snapshot event pattern (events counter, checkSnapshotData, JSON.parse(snapshot)), and the resolved guard safely handles the extra async client.config() check at the end. The assertions on disabledConfig, enabledConfig, and currentConfig give good coverage of the runtime config state around the snapshot request.

test/agents/test-zmq-profile.mjs (1)

133-148: Good coverage of assetsEnabled disable/enable flow

The sequence that disables assets, asserts the 422/Invalid arguments on agentProfileStart, then re-enables assets and starts a successful profile aligns well with the intended behavior and matches existing error-handling patterns in this file. No changes needed here.

test/parallel/test-nsolid-heap-sampling.js (1)

164-176: Sequencing of the new assets-toggle tests and platformTimeout usage looks sound

Placing runHeapSamplingAssetsToggleTests() inside the innermost heapSamplingEnd callback keeps the original error-path coverage intact and guarantees the new assets-toggling scenarios only run after the prior sequence completes. The switch to common.platformTimeout(100) on the surrounding timers is also in line with existing test patterns and should help with cross-platform stability.

test/agents/test-zmq-tracing.mjs (1)

406-472: LGTM! Test correctly implements phase-based tracing toggle verification.

The test properly:

  1. Fails immediately if spans arrive during the disabled phase (line 432)
  2. Sets phase = 'reenabled' after enableTraces() completes (lines 452-453), preventing misclassification of late spans
  3. Includes a 200ms delay to allow any pending spans to flush before re-enabling
agents/grpc/src/grpc_agent.h (1)

351-351: LGTM! Thread-safe atomic flag for asset control.

Using std::atomic<bool> is appropriate for the assets_enabled_ flag that will be accessed from multiple threads (configuration updates and profiling operations).

test/common/nsolid-zmq-agent/client.js (1)

141-148: LGTM! IPC handlers for asset/trace toggles.

The new handlers correctly delegate to the corresponding nsolid API methods. These are synchronous toggles that don't require acknowledgment responses.

test/agents/test-grpc-snapshot.mjs (3)

246-280: LGTM! Test correctly validates assets-disabled error via environment variable.

The test properly verifies that when NSOLID_ASSETS_ENABLED: '0' is set, heap snapshot requests return error code 500 with message 'Assets collection disabled(1008)'.


282-323: LGTM! Test validates programmatic assetsEnabled toggle via nsolid.start().

The test correctly:

  1. Disables assets via child.config({ assetsEnabled: false })
  2. Verifies snapshot request fails with the expected error
  3. Re-enables assets and confirms snapshot succeeds

325-367: LGTM! Test validates enableAssets()/disableAssets() helper methods.

The test properly exercises the helper API and confirms the config state reflects the toggle after enableAssets() (line 358-359).

agents/grpc/src/grpc_errors.h (1)

12-13: LGTM! New error type for assets-disabled state.

The EAssetsDisabled error with HTTP code 500, message "Assets collection disabled", and runtime code 1008 correctly aligns with test expectations across the test suite.

test/agents/test-zmq-heap-profile.mjs (1)

342-408: Test logic is sound with proper cleanup via resolved flag.

The resolved flag (line 349, 378-379, 399) correctly prevents processing stale events after the test resolves, avoiding potential race conditions.

test/agents/test-grpc-continuous-profile.mjs (3)

121-156: LGTM! Test validates no continuous profiles are emitted when assets disabled via environment.

The test correctly waits 300ms (slightly more than two 100ms intervals) to verify no profiles arrive, providing reasonable margin for CI timing variability.


186-193: Timing tolerance handles in-flight profiles gracefully.

The assertion profileCount - countAfterDisable <= 1 (line 189) correctly accounts for at most one in-flight profile that may arrive after disabling. This is a reasonable tolerance for async profiling systems.


203-245: LGTM! Test validates disableAssets()/enableAssets() for continuous profiling.

The test properly verifies the complete enable/disable/re-enable cycle and confirms the final config state reflects assetsEnabled: true (line 237).

test/parallel/test-nsolid-profile.js (1)

117-118: Good use of platformTimeout for CI adaptability.

Replacing fixed 100ms timeouts with common.platformTimeout(100) ensures the test adapts to slower CI environments, reducing flakiness.

test/agents/test-grpc-tracing.mjs (1)

185-247: Phase-based enable/disable tracing tests look correct and exercise the new API surface well

The new phase state machine for HTTP and custom tracing correctly:

  • Verifies initial spans content.
  • Clears accumulated spans and disables traces.
  • Confirms no spans arrive while disabled after an additional trace + delay.
  • Re-enables traces, triggers another trace, and validates spans again before clean shutdown.

The guards for phase === 'done' and use of mustCallAtLeast(..., 2) should keep the tests stable while allowing extra span batches if they arrive. No functional issues spotted in the new logic.

Also applies to: 253-315

test/parallel/test-nsolid-heap-profile.js (1)

124-204: Assets toggle coverage around heap profiling is thorough and consistent

runHeapProfileAssetsToggleTests() cleanly exercises:

  • Starting with assetsEnabled: false (start/stop both error, with and without callbacks).
  • Re-enabling assets and running a successful profile.
  • Disabling again and re-validating start/stop errors.
  • Final re-enable and successful profile/stop.

The use of common.platformTimeout(100) is consistent with other tests and should keep timing robust across platforms.

test/agents/test-grpc-heap-sampling.mjs (1)

248-378: Heap sampling assetsEnabled tests correctly mirror the intended error and toggle behavior

The three added tests:

  • Verify NSOLID_ASSETS_ENABLED='0' yields a 500 Assets collection disabled(1008) error.
  • Confirm assetsEnabled toggling via config() affects subsequent heapSampling calls as expected.
  • Exercise the enableAssets()/disableAssets() helpers and assert final assetsEnabled === true.

Assertions use the existing checkProfileError/checkProfileData helpers, so coverage is consistent with the rest of this suite.

test/agents/test-grpc-heap-profile.mjs (1)

259-389: Heap profile tests for assetsEnabled are well-aligned with heap sampling behavior

The new tests:

  • Cover the NSOLID_ASSETS_ENABLED=0 case and expect the 500 Assets collection disabled(1008) error.
  • Exercise dynamic toggling via child.config({ assetsEnabled }) and via disableAssets()/enableAssets() helpers.
  • Validate that when re-enabled, heap profiles complete successfully, and that assetsEnabled in the reported config is true.

They parallel the heap sampling tests and give good end-to-end coverage for asset gating.

agents/grpc/src/proto/reconfigure.pb.cc (1)

29-147: Generated ReconfigureBody gencode for assetsEnabled looks consistent

The additions for assetsenabled_ (constructor init, memcpy/memset ranges, offsets table, descriptor string, parse table, Clear/Merge/Serialize/ByteSize, and InternalSwap ranges) are coherent with introducing an optional bool assetsEnabled = 13 on ReconfigureBody. No manual inconsistencies jump out, so this file looks like correctly regenerated protobuf code.

Also applies to: 153-195, 339-429, 440-472, 581-587, 624-625, 702-715, 739-744

agents/grpc/src/grpc_agent.cc (2)

320-383: assetsEnabled propagation through reconfigure events and back into config is coherent

  • PopulateReconfigureEvent() now emits assetsEnabled when present in the JSON config.
  • reconfigure() maps body.assetsenabled() back into out["assetsEnabled"].
  • send_reconfigure_event() still uses PopulateReconfigureEvent so round-tripping works.

Together with the JSON config handling in config(), this ensures assetsEnabled flows cleanly between runtime config, GRPC requests, and events.

Also applies to: 1695-1747, 1931-1947


421-432: assets_enabled_ initialization and JSON handling are sensible and type-safe

  • GrpcAgent ctor now initializes assets_enabled_(true), matching the JS default and initializeConfig() logic.
  • config() reads assetsEnabled from config_ only when the JSON value is actually boolean, avoiding type issues.
  • The added debug JSON logs around config_ help trace configuration diffs.

Assuming assets_enabled_ is declared std::atomic<bool> in the header (as previously suggested), these assignments are also thread-safe.

Also applies to: 1000-1007, 1178-1183

test/common/nsolid-grpc-agent/index.js (1)

346-388: New TestClient helpers for config/assets/traces match the runtime toggles cleanly

The added methods:

  • config(config = null) returning msg.config,
  • enableAssets()/disableAssets(),
  • enableTraces()/disableTraces(), and
  • trace(kind, targetThreadId = 0)

provide a clean, promise-based API that mirrors the new assetsEnabled and tracing runtime controls in lib/nsolid.js. Call patterns in the GRPC tests (heap profile/sampling, tracing) are straightforward and consistent.

lib/nsolid.js (2)

84-154: Public API surface and getters for assetsEnabled/tracingEnabled are well-designed

  • Exposing enableAssets()/disableAssets() and enableTraces()/disableTraces() on the exported start object matches the rest of the NSolid config API.

  • The new getters

    assetsEnabled: () => getConfig('assetsEnabled') ?? true,
    tracingEnabled: () => getConfig('tracingEnabled') ?? false,

    give sensible defaults while still reflecting runtime config, including env and package.json sources.

This keeps the JS-facing API coherent with the underlying GRPC/ZMQ behavior.

Also applies to: 156-215


416-453: heapProfileStream/heapSamplingStream correctly respect assetsEnabled at the JS layer

Both streaming APIs now short-circuit when assets are disabled:

if (getConfig('assetsEnabled') === false)
  throw new ERR_NSOLID_HEAP_PROFILE_START(); // or ERR_NSOLID_HEAP_SAMPLING_START

This:

  • Gives immediate, deterministic failures for JS callers when assets are off, instead of relying on agent-side errors alone.
  • Matches the parallel GRPC tests that expect asset operations to be blocked globally when assetsEnabled is false.

The rest of the stream wiring (validation, destroy handlers, error propagation via the callback status) remains unchanged.

Also applies to: 501-543

agents/grpc/src/proto/reconfigure.pb.h (1)

233-233: Generated protobuf code properly adds assetsEnabled field.

The additions for the new assetsEnabled field (field number 13) are correctly generated and follow the established pattern of other boolean fields in the message. The has-bit mask 0x00000800U, accessor/mutator methods, struct member declaration, and TcParseTable update are all consistent with protobuf conventions.

Also applies to: 392-401, 408-408, 442-442, 1175-1201

@santigimeno santigimeno requested a review from EHortua December 2, 2025 14:21
Copy link
Copy Markdown

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/common/nsolid-grpc-agent/index.js (1)

389-407: Tighten #sendAndWait timeout behavior and payload construction

Two minor points here:

  1. Timeout → map(null) can throw / leave Promise unsettled

    On timeout you call resolve(map ? map(null) : undefined). For config(), map is msg => msg.config, so a timeout would call map(null) and throw before resolve runs, leaving the Promise pending and surfacing as an unhandled exception.

    Consider making the timeout path independent of map, e.g.:

    - const timeout = setTimeout(() => {
    -   this.#child.off('message', handler);
    -   resolve(map ? map(null) : undefined);
    - }, 30000);
    + const timeout = setTimeout(() => {
    +   this.#child.off('message', handler);
    +   resolve(undefined);  // or reject(new Error('config timeout'));
    + }, 30000);

    and let callers treat undefined/null as “no response” if they care.

  2. Payload can still override explicit type

    Sending { type, ...payload } means a payload.type property would overwrite the intended type. Swapping the order avoids this foot‑gun:

  •  this.#child.on('message', handler);
    
  •  this.#child.send({ type, ...payload });
    
  •  this.#child.on('message', handler);
    
  •  this.#child.send({ ...payload, type });
    
    
    That keeps the call‑site API unchanged but guarantees `type` is authoritative.
    
    
🧹 Nitpick comments (6)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

220-222: Remove redundant assertion.

The assert.ok(true) assertion is redundant since common.mustCall() already validates that the callback is invoked. Consider removing it or replacing it with a more meaningful check if additional validation is needed.

Apply this diff:

           finalStream.on('end', common.mustCall(() => {
-             assert.ok(true);
+             // Test passes if 'end' event fires (validated by mustCall)
           }));
lib/nsolid.js (2)

416-419: Consider validating arguments before checking assetsEnabled in stream APIs

heapProfileStream and heapSamplingStream now synchronously throw ERR_NSOLID_HEAP_PROFILE_START / ERR_NSOLID_HEAP_SAMPLING_START when assetsEnabled === false, which matches the “immediate state check throws” semantics.

Right now the assets check runs before the validators; this means invalid arguments will be masked by the generic “could not be started” error when assets are disabled. If you want to preserve the more precise argument errors regardless of assets state, consider moving the assetsEnabled check after the validateNumber / validateBoolean calls:

-function heapProfileStream(threadId, duration, trackAllocations, options) {
-  if (getConfig('assetsEnabled') === false)
-    throw new ERR_NSOLID_HEAP_PROFILE_START();
-
-  validateNumber(threadId, 'threadId');
-  validateNumber(duration, 'duration');
-  validateBoolean(trackAllocations, 'trackAllocations');
+function heapProfileStream(threadId, duration, trackAllocations, options) {
+  validateNumber(threadId, 'threadId');
+  validateNumber(duration, 'duration');
+  validateBoolean(trackAllocations, 'trackAllocations');
+  if (getConfig('assetsEnabled') === false)
+    throw new ERR_NSOLID_HEAP_PROFILE_START();

(and similarly for heapSamplingStream).

Also applies to: 501-504


765-769: optionToBool + assetsEnabled normalization: minor type-safety improvement

The assetsEnabled handling end‑to‑end is solid: env overrides package, defaulting to true, and updateConfig ignores unparseable values. One small polish:

  • In optionToBool, the string branch currently returns envToBool(value) directly. For empty strings, envToBool('') returns '', so envAssetsEnabled becomes '' and is treated as “set” (and truthy) even though it’s effectively “unset”.

If you want to avoid ever storing non‑boolean values in config for these toggles, you can coerce the string branch explicitly, and treat empty strings as “unspecified”:

function optionToBool(value) {
  if (value === undefined || value === null)
    return undefined;
  if (typeof value === 'boolean')
    return value;
  if (typeof value === 'number')
    return value !== 0;
-  if (typeof value === 'string')
-    return envToBool(value);
+  if (typeof value === 'string') {
+    if (value === '')
+      return undefined;
+    return !!envToBool(value);
+  }
  return undefined;
}

This keeps assetsEnabled strictly boolean/undefined in both initializeConfig and updateConfig.

Also applies to: 951-960, 1110-1120

test/agents/test-grpc-continuous-profile.mjs (1)

158-245: Continuous profiling assets toggle tests are well-structured but timing-sensitive

The tests that toggle assets via child.config({ assetsEnabled: ... }) and child.enableAssets()/disableAssets() correctly:

  • Assert at least one profile before disabling.
  • Allow for one in‑flight profile after disable.
  • Verify profiles resume after re‑enable and that currentConfig.assetsEnabled === true.

Given the 100 ms interval and 250 ms waits, the <= 1 post‑disable assertion could become flaky if disable propagation takes longer under load. If flakes appear, consider relaxing the bound slightly or increasing the delay rather than changing logic.

test/parallel/test-nsolid-heap-sampling.js (1)

164-176: Heap sampling assets toggle flow is correct but quite timing-heavy

runHeapSamplingAssetsToggleTests exercises:

  • Disabling assets via nsolid.start({ assetsEnabled: false }) and confirming both sync heapSampling() and callback forms fail, and heapSamplingEnd also fails.
  • Re‑enabling via nsolid.enableAssets(), running a full sampling + end cycle, then disabling again and re‑asserting the failures.
  • Finally re‑enabling and running one last successful sampling + end.

The nested setTimeout(..., common.platformTimeout(100)) calls are acceptable in this test suite, but they do make the flow a bit fragile to scheduling variance. If this ever turns flaky, consider slightly increasing the platform timeout or flattening the nesting rather than changing the logic.

Also applies to: 177-248

test/common/nsolid-grpc-agent/index.js (1)

346-387: New TestClient config/assets/traces helpers are consistent with existing API

  • config(config = null) now centrally uses #sendAndWait and returns msg.config, preserving previous semantics while reducing duplication.
  • enableAssets()/disableAssets() and enableTraces()/disableTraces() correctly guard on #child and use #sendAndWait so callers only resume once the child has acknowledged.
  • trace(kind, targetThreadId = 0) mirrors the existing tracing() method’s behavior (fire‑and‑forget with an await on the send callback), making it straightforward for tests to trigger spans.

The new helpers look correct and align with the rest of the TestClient surface.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1da77d and c879387.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (3 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (15)
  • agents/grpc/proto/reconfigure.proto
  • test/common/nsolid-zmq-agent/index.js
  • test/agents/test-grpc-profile.mjs
  • test/agents/test-zmq-profile.mjs
  • test/agents/test-zmq-tracing.mjs
  • agents/grpc/src/grpc_errors.h
  • test/parallel/test-nsolid-config-tracing.js
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-snapshot.mjs
  • src/nsolid/nsolid_api.cc
  • test/common/nsolid-zmq-agent/client.js
  • agents/zmq/src/zmq_agent.cc
  • test/agents/test-zmq-heap-profile.mjs
  • agents/grpc/src/grpc_agent.h
  • test/agents/test-grpc-snapshot.mjs
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-07-08T14:48:04.827Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., `test/addons/*/binding.cc`), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/agents/test-grpc-heap-sampling.mjs
  • test/agents/test-grpc-heap-profile.mjs
  • test/parallel/test-nsolid-heap-sampling-stream.js
📚 Learning: 2025-07-08T14:46:47.806Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-grpc-agent/client.js
  • test/agents/test-grpc-heap-sampling.mjs
  • test/agents/test-grpc-heap-profile.mjs
  • test/parallel/test-nsolid-heap-sampling-stream.js
📚 Learning: 2025-10-14T10:18:23.123Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 377
File: test/parallel/test-nsolid-heap-profile-stream.js:81-88
Timestamp: 2025-10-14T10:18:23.123Z
Learning: In nsolid.heapProfileStream() and nsolid.heapSamplingStream(), synchronous throws occur for argument validation errors and immediate state validation checks (like assets being disabled), while async error events are emitted for runtime errors that manifest during execution (like invalid threadId).

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile-stream.js
  • lib/nsolid.js
  • test/agents/test-grpc-heap-profile.mjs
  • test/parallel/test-nsolid-heap-sampling-stream.js
📚 Learning: 2025-07-08T14:47:34.724Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:16-18
Timestamp: 2025-07-08T14:47:34.724Z
Learning: In the nsolid test suite, external tool invocations (e.g., execSync to run `readelf`) are intentionally left uncaught so that any failure causes the test to fail rather than being skipped.

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-grpc-agent/client.js
  • test/agents/test-grpc-heap-sampling.mjs
  • test/agents/test-grpc-heap-profile.mjs
  • test/parallel/test-nsolid-heap-sampling-stream.js
📚 Learning: 2025-07-15T16:09:04.521Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js:47-73
Timestamp: 2025-07-15T16:09:04.521Z
Learning: In the nsolid test suite, hardcoded timeouts are acceptable for timing-sensitive tests. The preferred approach when tests become flaky is to increase timeout values rather than implementing more complex synchronization mechanisms.

Applied to files:

  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile-stream.js
📚 Learning: 2025-06-10T09:52:49.401Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling-stream.js
📚 Learning: 2025-08-20T11:03:11.870Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 354
File: test/agents/test-zmq-handshake.mjs:55-57
Timestamp: 2025-08-20T11:03:11.870Z
Learning: In test files, suggestions for general robustness improvements like try/finally blocks for teardown should be considered out of scope if they're not directly related to the main PR changes, and should be deferred to separate PRs or issues.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling-stream.js
🧬 Code graph analysis (11)
test/agents/test-zmq-heap-sampling.mjs (2)
test/agents/test-grpc-heap-sampling.mjs (3)
  • disabledConfig (308-308)
  • enabledConfig (319-319)
  • currentConfig (369-369)
test/agents/test-zmq-heap-profile.mjs (10)
  • playground (491-491)
  • profile (43-43)
  • profile (98-98)
  • profile (252-252)
  • profile (298-298)
  • profile (347-347)
  • profile (415-415)
  • disabledConfig (362-362)
  • enabledConfig (373-373)
  • currentConfig (400-400)
test/agents/test-grpc-tracing.mjs (1)
test/agents/test-zmq-tracing.mjs (1)
  • phase (411-411)
test/parallel/test-nsolid-heap-profile.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/parallel/test-nsolid-heap-sampling.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/agents/test-grpc-continuous-profile.mjs (2)
test/agents/test-grpc-heap-profile.mjs (22)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • child (414-414)
  • child (451-451)
  • currentConfig (380-380)
  • workers (136-136)
  • workers (238-238)
  • wid (137-137)
  • wid (239-239)
  • options (97-111)
  • options (138-152)
  • options (178-181)
  • options (206-209)
  • options (240-243)
  • options (275-281)
  • options (416-419)
test/agents/test-grpc-profile.mjs (6)
  • currentConfig (355-355)
  • workers (123-123)
  • workers (222-222)
  • wid (124-124)
  • wid (223-223)
  • options (87-98)
test/parallel/test-nsolid-profile.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/parallel/test-nsolid-heap-profile-stream.js (1)
test/parallel/test-nsolid-heap-profile.js (3)
  • nsolid (5-5)
  • assert (4-4)
  • common (3-3)
agents/grpc/src/proto/reconfigure.pb.h (1)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
test/agents/test-grpc-heap-sampling.mjs (1)
test/agents/test-grpc-heap-profile.mjs (32)
  • grpcServer (88-88)
  • grpcServer (113-113)
  • grpcServer (127-127)
  • grpcServer (154-154)
  • grpcServer (169-169)
  • grpcServer (183-183)
  • grpcServer (197-197)
  • child (95-95)
  • child (134-134)
  • child (176-176)
  • child (204-204)
  • child (236-236)
  • child (273-273)
  • child (309-309)
  • child (355-355)
  • agentId (96-96)
  • agentId (135-135)
  • agentId (177-177)
  • agentId (205-205)
  • agentId (237-237)
  • agentId (274-274)
  • agentId (310-310)
  • agentId (356-356)
  • agentId (415-415)
  • agentId (460-460)
  • profileOpts (311-317)
  • profileOpts (357-363)
  • disabledConfig (319-319)
  • disabledResult (322-322)
  • disabledResult (367-367)
  • enabledConfig (330-330)
  • currentConfig (380-380)
test/common/nsolid-grpc-agent/index.js (1)
test/common/nsolid-grpc-agent/client.js (1)
  • msg (89-89)
agents/grpc/src/proto/reconfigure.pb.cc (1)
agents/grpc/src/proto/reconfigure.pb.h (16)
  • ReconfigureBody (94-94)
  • ReconfigureBody (95-96)
  • ReconfigureBody (681-693)
  • ReconfigureBody (694-698)
  • ReconfigureBody (699-721)
  • ReconfigureBody (722-726)
  • ReconfigureBody (727-749)
  • ReconfigureBody (750-754)
  • ReconfigureBody (755-777)
  • ReconfigureBody (778-782)
  • ReconfigureBody (783-805)
  • ReconfigureBody (806-810)
  • ReconfigureBody (811-834)
  • ReconfigureBody (836-841)
  • ReconfigureBody (875-887)
  • ReconfigureBody (890-903)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test-macOS
  • GitHub Check: coverage-linux
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md
  • GitHub Check: build-tarball
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
🔇 Additional comments (15)
test/parallel/test-nsolid-heap-sampling-stream.js (1)

171-228: Well-structured asset toggle test coverage.

The test comprehensively exercises the asset enable/disable functionality through multiple toggle cycles, validating both synchronous error behavior when disabled and successful profile generation when enabled. The nested setImmediate calls appropriately sequence the async state transitions.

agents/zmq/src/zmq_agent.h (1)

728-732: New assets flag member placement looks correct

Adding assets_enabled_ alongside the profiling state is consistent with the new assets gating. As long as the constructor initializes it to a defined default (e.g., true) and config paths only flip it explicitly, this header-side change is fine.

lib/nsolid.js (2)

83-154: Public assets/tracing toggles are coherent with config plumbing

Exporting enableAssets / disableAssets and enableTraces / disableTraces on the start object, backed by updateConfig({ assetsEnabled|tracingEnabled: ... }), gives a clear and symmetric API surface. The early‑return guards avoid redundant config updates and look good.

Also applies to: 609-639


156-215: Getters for assetsEnabled/tracingEnabled have sensible defaults

assetsEnabled: () => getConfig('assetsEnabled') ?? true and tracingEnabled: () => getConfig('tracingEnabled') ?? false correctly preserve explicit false while defaulting when unset. This matches the intended “assets on by default, tracing off by default” behavior across config/env/package.

test/agents/test-grpc-continuous-profile.mjs (2)

4-4: Using timers/promises for timing in async tests looks good

Switching to await setTimeout(...) from node:timers/promises for the new “assets disabled via env” test is clean and consistent with the existing async style. The test asserts zero profiles, waits slightly >2 intervals, and correctly shuts down the child and GRPC server; no leaks apparent.

Also applies to: 121-156


255-267: Refactors in worker and “continuous during manual profile” tests are benign

  • Hoisting env, opts, child, and workers setup in the worker test keeps logic clearer without changing behavior.
  • Replacing callback‑style setTimeout with await setTimeout(...) in the last two tests simplifies flow and doesn’t affect semantics.

These changes look safe.

Also applies to: 380-381, 429-431

test/common/nsolid-grpc-agent/client.js (1)

59-81: gRPC test client: OTEL registration guard and assets/traces toggles look correct

  • Wrapping nsolid.otel.register(api) in a try/catch that only swallows ERR_NSOLID_OTEL_API_ALREADY_REGISTERED avoids noisy failures when the API has already been wired, while still surfacing real errors.
  • The new message types (enable_assets, disable_assets, enable_traces, disable_traces) cleanly proxy to nsolid.enableAssets()/disableAssets()/enableTraces()/disableTraces() and synchronously echo back an acknowledgement, which matches how the other commands in this client behave.

No issues spotted.

Also applies to: 120-137

test/agents/test-grpc-heap-sampling.mjs (1)

248-378: Assets-disabled heap sampling tests are consistent with GRPC error semantics

The three new tests:

  • Enforce that NSOLID_ASSETS_ENABLED=0 yields a 500 with Assets collection disabled(1008).
  • Verify that toggling assetsEnabled via child.config() and via enableAssets()/disableAssets() produces the same 500 error when disabled and valid data when re‑enabled.
  • Assert disabledConfig.assetsEnabled === false and final currentConfig.assetsEnabled === true, exercising the config surface end‑to‑end.

The expectations line up with the new EAssetsDisabled behavior and look thorough.

test/parallel/test-nsolid-profile.js (1)

107-119: CPU profiling assets toggle test mirrors heap-sampling behavior appropriately

runAssetsToggleTests:

  • Disables assets via nsolid.start({ assetsEnabled: false }) and validates both sync and callback profile() start failures plus profileEnd() stop failure.
  • Re‑enables with nsolid.enableAssets(), runs a full profile + end, then disables again and verifies start/stop failures once more before re‑enabling and doing a final successful cycle.

Using common.platformTimeout(100) for the staged delays is consistent with other timing tests. The logic matches the intended “assets gate all CPU profiling” semantics.

Also applies to: 120-182

test/parallel/test-nsolid-heap-profile.js (1)

122-132: Heap profile assets toggle helper is consistent and covers key paths

runHeapProfileAssetsToggleTests:

  • Starts by disabling assets via config and confirming both direct and callback heapProfile start failures plus heapProfileEnd stop failure.
  • Re‑enables via nsolid.enableAssets(), performs a full profile cycle, then disables again and re‑asserts failures for start/stop.
  • Finally re‑enables and runs a last successful profile + end, with all staged via common.platformTimeout(100) delays.

This gives good coverage of the heap profile API’s behavior under assets toggling, aligned with the rest of the suite.

Also applies to: 133-204

test/agents/test-grpc-tracing.mjs (1)

5-5: Phase-based tracing toggle logic looks correct and exercises disable/enable paths

The new phase state machine plus enableTraces()/disableTraces() and trace() calls are wired coherently: initial spans are validated, spans buffer is cleared before disabling, you assert that no spans arrive while disabled, and then you re-enable and validate spans again. Using mustCallAtLeast(..., 2) matches the two expected observation points and the 'done' phase guard avoids extra work on late events. The same pattern is correctly duplicated for custom tracing.

No functional issues spotted here.

Also applies to: 205-247, 273-315

test/agents/test-grpc-heap-profile.mjs (1)

259-389: Heap profile assetsEnabled tests are well‑structured and consistent

The three added tests cover the key surfaces:

  • Env flag NSOLID_ASSETS_ENABLED=0500 with "Assets collection disabled(1008)".
  • config({ assetsEnabled: false/true }) → error when disabled, valid profile when re‑enabled.
  • disableAssets()/enableAssets() → same behavior plus a final config() check that assetsEnabled is true.

Options, requestId plumbing, and shutdown/close flows match existing tests, so this gives good coverage of the new behavior without regressions.

agents/grpc/src/grpc_agent.cc (1)

379-383: Assets and continuous-CPU gating are wired correctly and concurrency‑safe

  • assets_enabled_ and cont_cpu_profile_enabled_ are now atomics, defaulting to true and false respectively, with store(..., std::memory_order_release) in config() and load(std::memory_order_acquire) in do_start_prof_init, which is appropriate for cross‑thread reads from *_from_js.
  • config() snapshots assetsEnabled and contCpuProfile from the JSON config with a boolean check before writing the atomics, avoiding the prior JSON data race for these flags in do_start_prof_init.
  • PopulateReconfigureEvent / reconfigure() correctly propagate the new assetsEnabled field in both directions, keeping the proto in sync with the runtime config.
  • do_start_prof_init now:
    • Short‑circuits with EAssetsDisabled when assets_enabled_ is false, before touching profile state, and
    • Refuses manual CPU profiles with EInProgressError when cont_cpu_profile_enabled_ is true,
      while leaving the rest of the profile bookkeeping unchanged.

This matches the intended semantics without introducing new synchronization issues.

Also applies to: 421-433, 1179-1191, 1747-1749, 2084-2151

agents/grpc/src/proto/reconfigure.pb.h (1)

219-234: Generated ReconfigureBody header correctly exposes assetsEnabled

The new assetsEnabled field is wired consistently:

  • Enum constant kAssetsEnabledFieldNumber = 13 matches the proto tag.
  • Public/inline accessors (has_assetsenabled, clear_assetsenabled, assetsenabled, set_assetsenabled) and internal variants use a new has‑bit (0x00000800U) and the _impl_.assetsenabled_ storage.
  • The parse table and layout (TcParseTable<4, 13, ...>, field entries) are updated to include this field.

Everything here looks mechanically consistent with the new proto definition.

Also applies to: 392-443, 1175-1202

agents/grpc/src/proto/reconfigure.pb.cc (1)

29-51: Generated ReconfigureBody implementation matches the new assetsEnabled field

  • _impl_.assetsenabled_ is initialized to false and included in the memcpy/memset ranges used in the copy ctor, SharedCtor, Clear(), and InternalSwap.
  • Descriptor data and schema include "assetsEnabled" with tag 13 and its presence bit is folded into the has‑bit masks (0x00000de0U).
  • The parse table and field metadata add an entry for field 13 that reads/writes _impl_.assetsenabled_, and _InternalSerialize writes it when the corresponding has‑bit is set.

This is all consistent with the header and proto and should serialize/parse assetsEnabled correctly.

Also applies to: 103-147, 153-194, 328-439, 440-472, 581-587, 611-625, 663-718

Comment thread test/agents/test-zmq-heap-sampling.mjs
Comment thread test/parallel/test-nsolid-heap-profile-stream.js Outdated
Copy link
Copy Markdown

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/common/nsolid-grpc-agent/index.js (1)

405-405: Payload spreading order still allows type override.

The previous review flagged that spreading payload after type allows payload.type to override the intended message type. This issue remains unresolved.

Apply this diff to ensure the explicit type cannot be overridden:

-      this.#child.send({ type, ...payload });
+      this.#child.send({ ...payload, type });
🧹 Nitpick comments (6)
test/parallel/test-nsolid-snapshot.js (2)

78-80: Optionally assert a specific error code for the assets‑disabled path

You currently assert err.code !== 0 and the error message. If the assets‑disabled snapshot path has (or will have) a stable, specific error code, consider also asserting assert.strictEqual(err.code, <EXPECTED_CODE>) here to make the test more precise and resilient to message changes. If there is no dedicated code, you can keep it as is.


68-109: Consider flattening nested setImmediate/callback pyramid for readability

The nested setImmediate + snapshot callbacks form a fairly deep pyramid. Not required, but you might consider extracting a small helper or sequencing via a simple async wrapper (e.g., promise‑wrapped snapshot) to make the control flow easier to follow, especially if more cases are added later.

test/agents/test-zmq-tracing.mjs (1)

5-5: ZMQ tracing toggle test now robustly enforces the disabled window.

The phase-driven flow plus assert.notStrictEqual(phase, 'disabled') ensures any spans emitted while tracing is disabled will immediately fail the test, and moving phase = 'reenabled' after enableTraces() avoids misclassifying late spans. The 200ms delay should be OK, though bumping it slightly (e.g. 300ms) would add margin against CI timing jitter if this ever flakes.

Also applies to: 406-472

test/agents/test-grpc-continuous-profile.mjs (1)

4-4: Continuous profiling tests correctly exercise assets gating and contCpuProfile interactions.

The new cases for env-based disable, config-based assetsEnabled toggling, and disableAssets()/enableAssets() all assert the expected presence/absence of profile events, with leeway for at most one in-flight sample after disabling and verification that profiles resume once assets are re-enabled. Migrating to await setTimeout(...) improves clarity over nested timers. Only minor nit: the comment in the “enable continuous profiling during an active CPU profile” test still mentions starting without continuous profiling even though the shared test env enables it from the outset.

Also applies to: 121-245, 255-266, 380-386, 430-446

agents/grpc/src/grpc_agent.cc (1)

379-382: Assets and continuous-CPU flags are now wired through config/reconfigure with safe cross-thread checks.

Including assetsEnabled in PopulateReconfigureEvent/reconfigure(), initializing the new atomics in the ctor, and snapshotting JSON assetsEnabled/contCpuProfile into assets_enabled_ / cont_cpu_profile_enabled_ in config() removes the earlier race for start_*_from_js and lets do_start_prof_init() cheaply gate:

  • all asset starts when assets are disabled (EAssetsDisabled), and
  • manual CPU profiles when continuous CPU profiling is enabled (EInProgressError).

The explicit is_boolean() guards before storing into the atomics are a nice touch to keep malformed config from throwing.

Also applies to: 421-433, 1179-1191, 1747-1750, 2084-2103

test/common/nsolid-grpc-agent/index.js (1)

391-393: Timeout resolves with undefined rather than rejecting.

When the timeout fires, the promise resolves with map(null) or undefined rather than rejecting. Callers must explicitly check for null/undefined to detect timeout, which is easy to miss.

Consider rejecting the promise on timeout for clearer error signaling:

      const timeout = setTimeout(() => {
        this.#child.off('message', handler);
-       resolve(map ? map(null) : undefined);
+       reject(new Error(`Timeout waiting for message type: ${expect}`));
      }, 30000); // 30 second timeout
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c879387 and a7459d3.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (3 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • test/common/nsolid-grpc-agent/client.js
  • test/common/nsolid-zmq-agent/client.js
  • agents/grpc/proto/reconfigure.proto
  • test/parallel/test-nsolid-heap-sampling-stream.js
  • agents/grpc/src/grpc_errors.h
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-zmq-agent/index.js
  • test/parallel/test-nsolid-config-tracing.js
  • agents/zmq/src/zmq_agent.h
  • test/agents/test-grpc-heap-sampling.mjs
  • test/agents/test-zmq-snapshot.mjs
  • test/agents/test-grpc-profile.mjs
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-10-14T10:18:23.123Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 377
File: test/parallel/test-nsolid-heap-profile-stream.js:81-88
Timestamp: 2025-10-14T10:18:23.123Z
Learning: In nsolid.heapProfileStream() and nsolid.heapSamplingStream(), synchronous throws occur for argument validation errors and immediate state validation checks (like assets being disabled), while async error events are emitted for runtime errors that manifest during execution (like invalid threadId).

Applied to files:

  • test/parallel/test-nsolid-heap-sampling.js
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • lib/nsolid.js
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-07-15T16:09:04.521Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js:47-73
Timestamp: 2025-07-15T16:09:04.521Z
Learning: In the nsolid test suite, hardcoded timeouts are acceptable for timing-sensitive tests. The preferred approach when tests become flaky is to increase timeout values rather than implementing more complex synchronization mechanisms.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-tracing.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-snapshot.js
📚 Learning: 2025-07-08T14:48:04.827Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., `test/addons/*/binding.cc`), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-snapshot.mjs
  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-zmq-profile.mjs
📚 Learning: 2025-07-08T14:47:34.724Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:16-18
Timestamp: 2025-07-08T14:47:34.724Z
Learning: In the nsolid test suite, external tool invocations (e.g., execSync to run `readelf`) are intentionally left uncaught so that any failure causes the test to fail rather than being skipped.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-snapshot.mjs
  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-grpc-heap-profile.mjs
📚 Learning: 2025-07-08T14:46:47.806Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.

Applied to files:

  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-snapshot.mjs
  • test/agents/test-zmq-heap-sampling.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-zmq-profile.mjs
  • test/agents/test-grpc-heap-profile.mjs
📚 Learning: 2025-06-10T09:52:49.401Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.

Applied to files:

  • test/agents/test-zmq-tracing.mjs
  • test/parallel/test-nsolid-snapshot.js
📚 Learning: 2025-12-02T14:38:52.884Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 377
File: test/agents/test-zmq-heap-sampling.mjs:135-201
Timestamp: 2025-12-02T14:38:52.884Z
Learning: In ZMQ agent tests, `playground.client.config(config)` exercises the nsolid.start() config surface by sending an IPC message to the child process, which then calls `nsolid.start(config)` before returning the current config. There is no `playground.child` API in the ZMQ test helpers.

Applied to files:

  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-zmq-heap-sampling.mjs
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-zmq-profile.mjs
📚 Learning: 2025-08-20T11:38:09.253Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 354
File: test/parallel/test-nsolid-config-saas.js:121-122
Timestamp: 2025-08-20T11:38:09.253Z
Learning: In the N|Solid codebase, when nsolid.js sets nsolidConfig.command = null, this null value is specifically designed to work with the C++ nsolid_api.cc UpdateConfig() method that uses nlohmann::json::merge_patch(). The merge patch operation with null results in the command field being undefined/removed in the final configuration, so tests should expect undefined rather than null for the final config values.

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
🧬 Code graph analysis (9)
test/parallel/test-nsolid-heap-sampling.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/parallel/test-nsolid-heap-profile.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
lib/nsolid.js (2)
lib/internal/process/pre_execution.js (1)
  • threadId (726-726)
agents/grpc/src/grpc_agent.cc (2)
  • config (1001-1194)
  • config (1001-1001)
agents/grpc/src/proto/reconfigure.pb.h (1)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
test/agents/test-grpc-tracing.mjs (1)
test/agents/test-zmq-tracing.mjs (1)
  • phase (411-411)
test/parallel/test-nsolid-snapshot.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/agents/test-zmq-heap-profile.mjs (2)
test/agents/test-grpc-heap-profile.mjs (3)
  • disabledConfig (319-319)
  • enabledConfig (330-330)
  • currentConfig (380-380)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
agents/grpc/src/proto/reconfigure.pb.cc (1)
agents/grpc/src/proto/reconfigure.pb.h (16)
  • ReconfigureBody (94-94)
  • ReconfigureBody (95-96)
  • ReconfigureBody (681-693)
  • ReconfigureBody (694-698)
  • ReconfigureBody (699-721)
  • ReconfigureBody (722-726)
  • ReconfigureBody (727-749)
  • ReconfigureBody (750-754)
  • ReconfigureBody (755-777)
  • ReconfigureBody (778-782)
  • ReconfigureBody (783-805)
  • ReconfigureBody (806-810)
  • ReconfigureBody (811-834)
  • ReconfigureBody (836-841)
  • ReconfigureBody (875-887)
  • ReconfigureBody (890-903)
test/agents/test-zmq-profile.mjs (1)
test/agents/test-zmq-snapshot.mjs (4)
  • playground (309-309)
  • disabledConfig (253-253)
  • enabledConfig (264-264)
  • currentConfig (288-288)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: coverage-linux
  • GitHub Check: test-macOS
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: build-tarball
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md
🔇 Additional comments (21)
test/parallel/test-nsolid-heap-sampling.js (3)

164-167: LGTM: Improved readability.

The multi-line formatting improves code readability without changing semantics.


168-168: LGTM: Proper test orchestration and timeout handling.

The new asset toggle test is integrated at the appropriate point after existing error path validation, and the platformTimeout wrapper correctly follows the established pattern for timing-sensitive tests.

Also applies to: 174-175


177-248: LGTM: Comprehensive asset toggle test coverage.

The new test function thoroughly validates the runtime asset control feature:

  • Correctly tests both synchronous throws and async callback errors when assets are disabled (consistent with the learning about immediate state validation)
  • Properly sequences operations to avoid race conditions by waiting for heap sampling completion before toggling state
  • Uses platformTimeout consistently for timing-sensitive operations
  • Validates the complete cycle: disable → verify errors → enable → successful operation → disable → verify errors → enable → final success

The deep nesting is consistent with the existing test style in this file.

agents/zmq/src/zmq_agent.cc (1)

662-662: LGTM!

The initialization of assets_enabled_ to true is correct, ensuring asset collection is enabled by default. Using std::atomic_bool provides thread-safe access.

test/parallel/test-nsolid-snapshot.js (1)

60-109: Snapshot/asset toggle sequencing now looks race‑free and aligned with intent

runSnapshotAssetsToggleTests enables/disables assets only after each preceding snapshot() call has either thrown synchronously or invoked its callback (mustCall/mustSucceed). This ordering should avoid the prior “snapshot already in progress” races while still exercising both error and success paths for assets being toggled at runtime. LGTM.

test/agents/test-zmq-heap-sampling.mjs (1)

135-201: Well-structured test for assetsEnabled toggle via nsolid.start().

The test correctly exercises the toggle flow: disabling assets returns a 422 error on heap sampling start, then re-enabling allows successful profiling. The use of playground.client.config() is appropriate as it sends an IPC message to the child process which calls nsolid.start(). Based on learnings, this is the correct API for ZMQ agent tests.

test/agents/test-zmq-profile.mjs (1)

120-177: Consistent test for assetsEnabled toggle in CPU profiling.

The test follows the same pattern as the heap sampling test, correctly validating that disabling assets returns a 422 error and re-enabling allows successful profiling. The implementation is consistent with other ZMQ agent tests.

src/nsolid/nsolid_api.cc (1)

1164-1190: Correct gating of continuous profiler by assetsEnabled.

The logic correctly ensures that when assetsEnabled is false, continuous CPU profiling is disabled (contCpuProfile defaults to false). Only when assetsEnabled is true are the actual contCpuProfile and contCpuProfileInterval values read from the configuration. This properly integrates the new asset control with the existing continuous profiler functionality.

lib/nsolid.js (5)

133-136: New public API surface for asset and trace control.

The four new helper methods (enableAssets, disableAssets, enableTraces, disableTraces) are appropriately added to the exported start object, providing a clean programmatic API for runtime control.


201-214: Correct getter implementations with appropriate defaults.

The assetsEnabled getter defaults to true (via nullish coalescing) and tracingEnabled defaults to false, which aligns with the expected behavior that assets are enabled by default while tracing must be explicitly enabled.


416-419: Synchronous throws for immediate state validation.

The checks throw synchronously when assetsEnabled is false, which is consistent with the established pattern where immediate state validation errors are thrown synchronously while runtime errors are emitted asynchronously. Based on learnings, this is the expected behavior for these stream APIs.

Also applies to: 502-504


609-638: Enable/disable helper implementations are idempotent.

Each helper checks the current state before calling updateConfig, preventing unnecessary configuration updates when the state is already as desired. This is a good practice for avoiding redundant work and potential side effects.


1110-1120: Robust type normalization with optionToBool.

The optionToBool helper correctly handles various input types: booleans pass through, numbers check for non-zero, strings delegate to envToBool, and null/undefined return undefined to signal "not specified". This enables flexible configuration from multiple sources.

test/agents/test-zmq-heap-profile.mjs (1)

342-408: Consistent assetsEnabled toggle test for heap profiling.

The test follows the established pattern across ZMQ agent tests, correctly validating the toggle flow for heap profiles. The explicit trackAllocations: false and corresponding assertions for trace_function_count, trace_function_infos, and trace_tree being zero/empty are appropriate for this test case.

test/parallel/test-nsolid-profile.js (2)

117-118: Good use of platformTimeout for cross-platform reliability.

Replacing hardcoded 100 with common.platformTimeout(100) improves test reliability across different platforms with varying performance characteristics. Based on learnings, this is the preferred approach.


120-182: Comprehensive assets toggle tests for CPU profiling.

The runAssetsToggleTests function thoroughly exercises the assetsEnabled control:

  1. Disables assets via nsolid.start() config and verifies sync/async errors
  2. Re-enables via nsolid.enableAssets() helper and verifies successful profiling
  3. Disables via nsolid.disableAssets() helper and verifies errors return
  4. Re-enables and completes a final successful profile cycle

This covers both configuration surfaces (start config and helper methods) and both error patterns (sync throws and async callbacks).

test/parallel/test-nsolid-heap-profile.js (1)

124-204: Heap profile assets toggle test flow looks correct and well-integrated.

The new runHeapProfileAssetsToggleTests() sequence cleanly validates sync/async error behavior with assetsEnabled off, re-enables via enableAssets(), and only toggles back to disabled after an ongoing profile completes. Using common.platformTimeout(100) for all waits should keep this stable across platforms. Based on learnings, this matches the pattern that immediate state validation (assets disabled) throws synchronously while runtime failures surface via the callback.

agents/grpc/src/grpc_agent.h (1)

351-352: Atomic profiling flags are appropriate here.

Declaring assets_enabled_ and cont_cpu_profile_enabled_ as atomics is consistent with other flags and supports the cross-thread checks in do_start_prof_init without further locking.

test/agents/test-grpc-snapshot.mjs (1)

246-367: Snapshot tests cover assetsEnabled across env, config, and helpers effectively.

The three new cases correctly assert the 500 / Assets collection disabled(1008) error when assets are off, and they verify that both config({ assetsEnabled }) and disableAssets()/enableAssets() round-trip the flag and restore successful snapshots afterwards.

test/agents/test-grpc-heap-profile.mjs (1)

259-389: Heap-profile assets gating tests are consistent and symmetric with snapshot coverage.

These three tests correctly validate that heap profiling returns 500 / Assets collection disabled(1008) when assets are disabled via env, config({ assetsEnabled: false }), or disableAssets(), and that re-enabling restores normal profile data. The use of heapProfile.trackAllocations keeps behavior explicit but simple.

test/agents/test-grpc-tracing.mjs (1)

5-5: gRPC tracing tests now validate disabled and reenabled phases explicitly.

The added phase logic around disableTraces()/enableTraces() ensures HTTP and custom tracing each prove three properties: initial spans are valid, no spans arrive during a 200ms disabled window (resourceSpans remains empty), and spans resume with the same structure after re-enabling. Importing delay from timers/promises keeps the flow readable and consistent with the ZMQ tracing toggle test.

Also applies to: 205-247, 273-315

Comment thread agents/zmq/src/zmq_agent.cc
Comment thread agents/zmq/src/zmq_agent.cc
Add comprehensive runtime control for asset collection (CPU/heap
profiling, snapshots) with multiple configuration methods.

Implements assetsEnabled flag with full control surface:
- NSOLID_ASSETS_ENABLED environment variable
- nsolid.start({ assetsEnabled: true/false }) configuration
- Runtime helpers: enableAssets() and disableAssets()
- Dynamic updates via gRPC reconfigure protocol
- Validation in both gRPC and ZMQ agents with proper errors
- Continuous profiler integration respects assetsEnabled state

This allows operators to disable expensive profiling operations
at runtime without process restarts, improving resource management
and production safety.

Also adds missing enableTraces()/disableTraces() helper methods
to complete the tracing API surface.

Comprehensive test coverage added for all asset types across gRPC,
ZMQ, and parallel test suites.
Copy link
Copy Markdown

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
agents/zmq/src/zmq_agent.cc (1)

1264-1288: Consider adding a type guard when reading assetsEnabled from config_.

The new block correctly wires the top‑level assetsEnabled config into assets_enabled_, but it assigns the JSON value directly without checking is_boolean(). If an invalid type ever sneaks in via the initial config (as opposed to the reconfigure path, which you now validate), this could throw or yield surprising behavior depending on the JSON library’s conversion rules.

You might want to mirror the validate_reconfigure() check here, e.g. ignore/log non‑boolean values and only update assets_enabled_ when the value is actually a boolean.

test/common/nsolid-grpc-agent/index.js (1)

346-407: Centralized send/wait helper is good; keep payload/type ordering and timeout behavior in mind

The new helpers (config, enable/disable assets & traces, trace) built on #sendAndWait make the TestClient much easier to use and extend, and the 30s timeout prevents hung tests.

Two caveats (one previously noted):

  • Payload spread order (previously flagged):
    this.#child.send({ type, ...payload }); still lets payload.type override the explicit type. Today your payloads don’t contain type, but for robustness it’s safer to send { ...payload, type } so the call site’s type can’t be clobbered.

  • Timeout behavior (optional):
    For methods without a map (e.g., enableAssets()), a timeout resolves the promise silently after 30s. If you ever need stronger guarantees, consider rejecting with an Error on timeout so tests fail fast when the client stops responding, and optionally guard this.#child in the timeout/handler to avoid off on null after child exit.

These are refinements; the overall structure and new API surface look solid.

🧹 Nitpick comments (5)
test/parallel/test-nsolid-heap-sampling.js (1)

177-248: Assets toggle coverage looks solid; consider reducing nesting if it grows further

The new runHeapSamplingAssetsToggleTests() nicely exercises the key transitions:

  • Disabling assets via nsolid.start({ assetsEnabled: false }) and validating both the sync heapSampling() throw path and async error callback path.
  • Re‑enabling with nsolid.enableAssets() and confirming a full successful heapSampling / heapSamplingEnd cycle.
  • Disabling again with nsolid.disableAssets() and re‑validating the error behavior, then finally re‑enabling and confirming success.

The sequencing (waiting for callbacks before toggling, and using common.platformTimeout(100) between config changes and assertions) keeps race windows small and matches existing patterns in related asset tests. From a correctness standpoint this looks good.

If you touch this again, you might consider a small optional refactor (e.g., extracting the repeated “assert start/stop fail” block into a tiny helper or using named inner functions) to shave some callback/timeout nesting, but it’s not required for this PR.

Based on learnings, this matches the expected sync/async error patterns for state validation in these APIs.

test/agents/test-grpc-tracing.mjs (2)

185-248: HTTP tracing phase machine correctly validates enable/disable behavior

The new phase‑based logic plus mustCallAtLeast(..., 2) cleanly exercises:

  • Initial tracing on (phase === 'initial') and validates the HTTP spans.
  • Runtime disableTraces() plus an extra trace('http'), asserting no spans arrive after a 200ms delay.
  • Re‑enabling traces and confirming spans are emitted again before shutting down the client.

Control flow and use of resourceSpans.length = 0 avoids stale data, and the phase === 'done' guard prevents post‑completion noise. Consider (optional) using a common.platformTimeout(200)-style helper instead of a hard-coded 200ms if these ever get flaky.


253-315: Custom tracing toggle flow mirrors HTTP path and looks sound

The custom tracing test mirrors the HTTP test’s phase machine:

  • Validates the expected single custom span when tracing is initially enabled.
  • Disables traces and confirms no spans arrive after emitting another custom trace and waiting 200ms.
  • Re‑enables, sends another trace, and validates spans again before shutdown.

The symmetry with the HTTP case makes behavior easy to reason about and keeps tests consistent. Same optional note as above about potentially using a platform‑scaled delay helper if timing ever proves flaky.

agents/grpc/src/grpc_agent.cc (1)

320-383: gRPC agent assets/tracing gating is wired correctly across config and profiling

  • Constructor + config():

    • Initializing assets_enabled_ to true and cont_cpu_profile_enabled_ to false, then updating them in config() via is_boolean() checks and store(..., std::memory_order_release) is a solid pattern for safely reflecting JSON config in atomics.
    • Reads in do_start_prof_init() use load(std::memory_order_acquire), which is appropriate for this simple flag scenario.
  • Reconfigure/propagation:

    • PopulateReconfigureEvent() now includes assetsEnabled from GetConfig(), and reconfigure() mirrors that by propagating body.assetsenabled() back into the JS‑visible config via UpdateConfig. This keeps console reconfigure, global config, and agent state consistent for the new flag.
  • Profiling gating (do_start_prof_init):

    • Early return with EAssetsDisabled when assets_enabled_ is false correctly prevents all manual asset profiles from starting and will surface as the expected JS/console errors.
    • The continuous CPU guard (type == kCpu && cont_cpu_profile_enabled_.load(...) → EInProgressError) avoids overlapping manual CPU profiles with the continuous profiler, and importantly no longer touches config_ off the agent thread.

Overall this closes the previously noted race on assetsEnabled/contCpuProfile while providing a clear, centralized gate for asset collection. Only minor nit (optional): PopulateReconfigureEvent sets assetsenabled without an is_boolean() check, unlike config(); if malformed config is a concern there too, you could mirror the same type guard.

Also applies to: 421-433, 1001-1194, 1703-1750, 2085-2152

lib/nsolid.js (1)

133-137: JS‑level assets/tracing toggles and stream gating are coherent with agent behavior

  • Public API & getters:

    • Exposing enableAssets/disableAssets and enableTraces/disableTraces on start and wiring them through updateConfig is the right abstraction, and the cheap getConfig(...) === true/false short‑circuit avoids redundant config updates.
    • The assetsEnabled and tracingEnabled getters with ?? defaults (true / false) line up with the initialization logic and keep nsolid.config/info consumers simple.
  • Heap stream gating:

    • The early checks in heapProfileStream / heapSamplingStream that throw ERR_NSOLID_HEAP_PROFILE_START / ERR_NSOLID_HEAP_SAMPLING_START when assetsEnabled === false match the documented behavior that immediate state validation errors (like assets disabled) are synchronous, while runtime failures still surface via the stream’s error path. This is consistent with the existing test-suite expectations. Based on learnings.
  • Config normalization:

    • initializeConfig’s tri‑state handling of NSOLID_ASSETS_ENABLED and pkgConfig.nsolid.assetsEnabled via optionToBool, plus a default of true when unset, ensures explicit false is preserved rather than overridden by || chains.
    • The updateConfig branch for assetsEnabled reuses optionToBool so JS APIs accept booleans, numbers, and env‑style strings without leaking odd types into the native config.

Overall the wiring between JS config, the global config store, and the C++ agents looks correct. Only tiny nit: heapProfileStream’s new options parameter is currently unused; you can drop it or hook it up later when you actually need extra options.

Also applies to: 201-215, 416-419, 501-504, 609-639, 951-961, 1110-1120

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7459d3 and 8a8eb19.

📒 Files selected for processing (32)
  • agents/grpc/proto/reconfigure.proto (1 hunks)
  • agents/grpc/src/grpc_agent.cc (5 hunks)
  • agents/grpc/src/grpc_agent.h (1 hunks)
  • agents/grpc/src/grpc_errors.h (1 hunks)
  • agents/grpc/src/proto/reconfigure.pb.cc (18 hunks)
  • agents/grpc/src/proto/reconfigure.pb.h (4 hunks)
  • agents/zmq/src/zmq_agent.cc (5 hunks)
  • agents/zmq/src/zmq_agent.h (1 hunks)
  • lib/nsolid.js (8 hunks)
  • src/nsolid/nsolid_api.cc (1 hunks)
  • test/agents/test-grpc-continuous-profile.mjs (6 hunks)
  • test/agents/test-grpc-heap-profile.mjs (1 hunks)
  • test/agents/test-grpc-heap-sampling.mjs (1 hunks)
  • test/agents/test-grpc-profile.mjs (1 hunks)
  • test/agents/test-grpc-snapshot.mjs (1 hunks)
  • test/agents/test-grpc-tracing.mjs (3 hunks)
  • test/agents/test-zmq-heap-profile.mjs (1 hunks)
  • test/agents/test-zmq-heap-sampling.mjs (1 hunks)
  • test/agents/test-zmq-profile.mjs (1 hunks)
  • test/agents/test-zmq-snapshot.mjs (1 hunks)
  • test/agents/test-zmq-tracing.mjs (2 hunks)
  • test/common/nsolid-grpc-agent/client.js (2 hunks)
  • test/common/nsolid-grpc-agent/index.js (1 hunks)
  • test/common/nsolid-zmq-agent/client.js (1 hunks)
  • test/common/nsolid-zmq-agent/index.js (1 hunks)
  • test/parallel/test-nsolid-config-tracing.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile-stream.js (1 hunks)
  • test/parallel/test-nsolid-heap-profile.js (1 hunks)
  • test/parallel/test-nsolid-heap-sampling-stream.js (2 hunks)
  • test/parallel/test-nsolid-heap-sampling.js (1 hunks)
  • test/parallel/test-nsolid-profile.js (1 hunks)
  • test/parallel/test-nsolid-snapshot.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
  • test/parallel/test-nsolid-heap-profile-stream.js
  • test/common/nsolid-zmq-agent/client.js
  • test/agents/test-zmq-heap-profile.mjs
  • test/agents/test-zmq-profile.mjs
  • test/parallel/test-nsolid-config-tracing.js
  • agents/zmq/src/zmq_agent.h
  • test/agents/test-zmq-tracing.mjs
  • test/agents/test-grpc-profile.mjs
  • test/common/nsolid-zmq-agent/index.js
  • agents/grpc/src/grpc_agent.h
  • test/agents/test-grpc-heap-sampling.mjs
  • test/parallel/test-nsolid-heap-sampling-stream.js
  • test/agents/test-grpc-heap-profile.mjs
  • test/common/nsolid-grpc-agent/client.js
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-07-08T14:46:47.806Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:13-14
Timestamp: 2025-07-08T14:46:47.806Z
Learning: In the nsolid test suite, native addon bindings are expected to be built beforehand; tests do not add fs.existsSync guards to skip when bindings are missing.

Applied to files:

  • test/agents/test-grpc-snapshot.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-snapshot.mjs
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-07-08T14:47:34.724Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/nsolid-elf-utils.js:16-18
Timestamp: 2025-07-08T14:47:34.724Z
Learning: In the nsolid test suite, external tool invocations (e.g., execSync to run `readelf`) are intentionally left uncaught so that any failure causes the test to fail rather than being skipped.

Applied to files:

  • test/agents/test-grpc-snapshot.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-snapshot.mjs
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-07-08T14:48:04.827Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 339
File: test/addons/nsolid-elf-utils/binding.cc:13-28
Timestamp: 2025-07-08T14:48:04.827Z
Learning: In nsolid test native addons (e.g., `test/addons/*/binding.cc`), additional JavaScript-facing argument validation is typically omitted because the tests supply well-formed inputs.

Applied to files:

  • test/agents/test-grpc-snapshot.mjs
  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/agents/test-grpc-continuous-profile.mjs
  • test/parallel/test-nsolid-snapshot.js
  • test/agents/test-zmq-snapshot.mjs
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-07-15T16:09:04.521Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/nsolid-code-event-hook.js:47-73
Timestamp: 2025-07-15T16:09:04.521Z
Learning: In the nsolid test suite, hardcoded timeouts are acceptable for timing-sensitive tests. The preferred approach when tests become flaky is to increase timeout values rather than implementing more complex synchronization mechanisms.

Applied to files:

  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • test/parallel/test-nsolid-snapshot.js
  • test/parallel/test-nsolid-heap-sampling.js
📚 Learning: 2025-10-14T10:18:23.123Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 377
File: test/parallel/test-nsolid-heap-profile-stream.js:81-88
Timestamp: 2025-10-14T10:18:23.123Z
Learning: In nsolid.heapProfileStream() and nsolid.heapSamplingStream(), synchronous throws occur for argument validation errors and immediate state validation checks (like assets being disabled), while async error events are emitted for runtime errors that manifest during execution (like invalid threadId).

Applied to files:

  • test/parallel/test-nsolid-profile.js
  • test/parallel/test-nsolid-heap-profile.js
  • lib/nsolid.js
  • test/parallel/test-nsolid-heap-sampling.js
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-10-27T13:06:17.506Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 0
File: :0-0
Timestamp: 2025-10-27T13:06:17.506Z
Learning: In the nodesource/nsolid repository, when reviewing PRs that add or modify V8 patches (e.g., in deps/v8/patches/), focus review comments on the patch management infrastructure (configure.py, apply-patches.sh, README.md, etc.) and not on the V8 code changes contained within the .patch files themselves, as the patch content is out of scope for the patch management PR.

Applied to files:

  • lib/nsolid.js
📚 Learning: 2025-06-10T09:52:49.401Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.

Applied to files:

  • test/parallel/test-nsolid-snapshot.js
📚 Learning: 2025-12-02T14:38:52.884Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 377
File: test/agents/test-zmq-heap-sampling.mjs:135-201
Timestamp: 2025-12-02T14:38:52.884Z
Learning: In ZMQ agent tests, `playground.client.config(config)` exercises the nsolid.start() config surface by sending an IPC message to the child process, which then calls `nsolid.start(config)` before returning the current config. There is no `playground.child` API in the ZMQ test helpers.

Applied to files:

  • test/agents/test-zmq-snapshot.mjs
  • test/agents/test-zmq-heap-sampling.mjs
📚 Learning: 2025-08-20T11:38:09.253Z
Learnt from: santigimeno
Repo: nodesource/nsolid PR: 354
File: test/parallel/test-nsolid-config-saas.js:121-122
Timestamp: 2025-08-20T11:38:09.253Z
Learning: In the N|Solid codebase, when nsolid.js sets nsolidConfig.command = null, this null value is specifically designed to work with the C++ nsolid_api.cc UpdateConfig() method that uses nlohmann::json::merge_patch(). The merge patch operation with null results in the command field being undefined/removed in the final configuration, so tests should expect undefined rather than null for the final config values.

Applied to files:

  • test/agents/test-zmq-heap-sampling.mjs
🧬 Code graph analysis (9)
test/parallel/test-nsolid-profile.js (2)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
agents/grpc/src/grpc_errors.h (1)
  • nsolid (16-33)
agents/grpc/src/grpc_agent.cc (1)
agents/zmq/src/zmq_agent.cc (10)
  • config (430-476)
  • config (430-430)
  • config (1264-1379)
  • config (1264-1264)
  • config (2767-2802)
  • config (2767-2767)
  • config (2895-2912)
  • config (2895-2895)
  • config (2990-3007)
  • config (2990-2990)
test/agents/test-grpc-tracing.mjs (1)
test/agents/test-zmq-tracing.mjs (1)
  • phase (411-411)
test/common/nsolid-grpc-agent/index.js (1)
test/common/nsolid-grpc-agent/client.js (1)
  • msg (89-89)
lib/nsolid.js (2)
agents/grpc/src/grpc_agent.cc (2)
  • config (1001-1194)
  • config (1001-1001)
agents/zmq/src/zmq_agent.cc (10)
  • config (430-476)
  • config (430-430)
  • config (1264-1379)
  • config (1264-1264)
  • config (2767-2802)
  • config (2767-2767)
  • config (2895-2912)
  • config (2895-2895)
  • config (2990-3007)
  • config (2990-2990)
agents/grpc/src/proto/reconfigure.pb.h (1)
agents/grpc/src/proto/reconfigure.pb.cc (4)
  • ReconfigureBody (53-53)
  • ReconfigureBody (267-270)
  • _impl_ (59-60)
  • _impl_ (85-86)
test/agents/test-zmq-snapshot.mjs (1)
test/agents/test-grpc-snapshot.mjs (3)
  • disabledConfig (300-300)
  • enabledConfig (311-311)
  • currentConfig (358-358)
test/parallel/test-nsolid-heap-sampling.js (1)
lib/nsolid.js (2)
  • err (447-447)
  • err (537-537)
test/agents/test-zmq-heap-sampling.mjs (1)
test/agents/test-grpc-heap-sampling.mjs (4)
  • options (86-100)
  • options (127-141)
  • disabledConfig (308-308)
  • enabledConfig (319-319)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: coverage-linux
  • GitHub Check: test-macOS
  • GitHub Check: build-tarball
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: coverage-windows
  • GitHub Check: lint-js-and-md
🔇 Additional comments (23)
test/agents/test-zmq-snapshot.mjs (1)

235-296: New ZMQ snapshot assetsEnabled toggle test correctly exercises config and event flow

The test wiring looks solid and consistent with the existing patterns:

  • Uses playground.client.config({ assetsEnabled: false/true }) to drive the nsolid.start() config surface, matching how ZMQ tests exercise config elsewhere (per prior heap-sampling tests, this is the expected API surface in ZMQ tests, not playground.child).
  • Validates the disabled path via a single agentSnapshotRequest with a mustCall error callback, asserting the expected 422/'Invalid arguments' contract that other ZMQ snapshot error tests in this file also rely on.
  • After re-enabling, it issues a snapshot without a callback and uses the familiar events counter + checkSnapshotData + snapshot concatenation pattern already used in the earlier snapshot tests, then confirms assetsEnabled stays true via a final playground.client.config() call.
  • The resolved guard in the async event handler is a reasonable extra safety to avoid double-resolving the outer Promise if any unexpected extra events arrive.

If you want to tighten things further, you could optionally assert some property of the parsed snapshot JSON (beyond just that it parses) to make the success path slightly stronger, but as-is this test already gives good coverage of the assetsEnabled gating behavior and config surface.

test/parallel/test-nsolid-heap-sampling.js (2)

164-168: Good integration point for assets toggle sub-tests

Triggering runHeapSamplingAssetsToggleTests() only after the final 'Heap sampling could not be stopped' assertion ensures the earlier sampling lifecycle tests complete before you start exercising the assetsEnabled transitions, which keeps state interactions predictable. The expanded multi-line assert.strictEqual is also clearer and consistent with nearby style.


174-176: Using common.platformTimeout(100) improves cross-platform stability

Switching from literal 100 to common.platformTimeout(100) keeps the original intent while respecting the test timeout scaling used in this suite, which should reduce flakiness on slower environments.

agents/zmq/src/zmq_agent.cc (3)

646-664: Defaulting assets_enabled_ to true preserves existing behavior.

Initializing assets_enabled_ to true in the ctor matches the previous implicit “assets always on” behavior and makes the new flag opt‑out only, which is consistent with the PR intent.


2113-2118: Central assets gating in do_start_prof_init looks good.

Putting the assets_enabled_ check at the top of do_start_prof_init() cleanly gates all profile/snapshot entry points (both console‑initiated and *_from_js helpers) behind a single flag, which aligns with the PR’s runtime control objective.


2494-2519: Reconfigure support and boolean validation for assetsEnabled are consistent.

Adding "assetsEnabled" to allowed_opts and enforcing val.is_boolean() in validate_reconfigure() ensures the flag is actually reconfigurable over ZMQ and prevents bad types from being applied, returning a clear 422 error with a specific message when invalid.

test/agents/test-zmq-heap-sampling.mjs (1)

135-201: LGTM! Well-structured test for assetsEnabled toggle behavior.

The test correctly exercises the assetsEnabled toggle via playground.client.config(), which properly invokes nsolid.start() in the child process via IPC (based on learnings). The flow appropriately verifies:

  1. Assets disabled → heap sampling fails with 422
  2. Assets re-enabled → heap sampling succeeds
  3. Final config state is validated

The resolved guard prevents double-resolution race conditions.

src/nsolid/nsolid_api.cc (1)

1164-1190: LGTM! Correct gating of continuous profiler settings behind assetsEnabled.

The logic properly:

  1. Triggers on changes to assetsEnabled, contCpuProfile, or contCpuProfileInterval
  2. Defaults assetsEnabled to true for backward compatibility
  3. Only reads profiler settings when assets are enabled; otherwise uses defaults that disable profiling
  4. Calls update_continuous_profiler() with the resolved values

This ensures the continuous profiler is stopped when assets are disabled and respects profiler settings when assets are enabled.

agents/grpc/proto/reconfigure.proto (1)

20-20: LGTM! Clean proto field addition.

The optional bool assetsEnabled field follows the established pattern in the message and uses an appropriate sequential field number.

agents/grpc/src/grpc_errors.h (1)

12-13: LGTM! Appropriate error definition for assets-disabled state.

The new EAssetsDisabled error follows the established macro pattern, uses a sequential runtime code (1008), and aligns with similar errors like ESnapshotDisabled in using HTTP status 500.

test/parallel/test-nsolid-snapshot.js (1)

60-110: LGTM! Well-sequenced test for assets toggle with snapshots.

The test properly addresses the previous race condition concern by:

  1. Waiting for snapshot completion callbacks before toggling assets state
  2. Using common.mustSucceed() to verify successful operations
  3. Chaining operations through callbacks to ensure deterministic ordering

The nested structure, while deep, correctly ensures each snapshot completes before the next asset state change.

test/agents/test-grpc-continuous-profile.mjs (4)

121-156: LGTM! Good test for environment-variable-based asset disabling.

The test correctly verifies that no continuous profiles are emitted when NSOLID_ASSETS_ENABLED: '0' is set in the environment. The 300ms wait (slightly longer than two 100ms intervals) is appropriate for validating no emissions occur.


158-201: LGTM! Well-designed test for config-based asset toggle.

The test properly validates:

  1. Initial profiling emits at least once
  2. After disabling via config, at most one additional profile arrives (in-flight tolerance)
  3. After re-enabling, profiling resumes with increased count

The <= 1 tolerance at line 189 appropriately accounts for an in-flight profile during the transition.


203-245: LGTM! Comprehensive test for helper-based asset toggle.

This test exercises disableAssets()/enableAssets() helpers and validates both the profiling behavior and final config state verification at line 237.


247-294: LGTM! Clean refactor of worker threads test.

Inlining the environment setup and removing duplicated initialization improves readability while maintaining the same test coverage.

test/agents/test-grpc-snapshot.mjs (3)

246-280: LGTM! Proper test for env-based asset disabling.

The test correctly validates that heap snapshots return error code 500 with the expected "Assets collection disabled(1008)" message when NSOLID_ASSETS_ENABLED: '0' is set.


282-323: LGTM! Good test for config-based asset toggle with snapshots.

The test properly validates both the disabled and enabled paths, verifying config state assertions at lines 301 and 312.


325-367: LGTM! Complete test for helper-based asset toggle with snapshots.

The test exercises the full cycle: disable → verify error → enable → verify success → verify final config state at line 359.

test/parallel/test-nsolid-profile.js (2)

117-118: Good improvement: using platformTimeout for CI stability.

Replacing hardcoded timeouts with common.platformTimeout(100) improves test reliability across different CI environments and platforms.


120-182: LGTM! Well-structured test for profile assets toggle.

The test properly sequences operations:

  1. Disables assets via config → verifies profile/profileEnd fail
  2. Enables via helper → starts and completes profile before next toggle
  3. Disables via helper → verifies profile fails
  4. Re-enables → verifies profile succeeds

The nested callbacks with common.mustSucceed() ensure each profile operation completes before toggling asset state, avoiding race conditions.

test/parallel/test-nsolid-heap-profile.js (1)

124-204: Assets toggle heap-profile flow looks correct and race‑safe

The new runHeapProfileAssetsToggleTests() sequence correctly:

  • Disables assets via nsolid.start({ assetsEnabled: false }) and asserts both sync and callback error paths for heapProfile / heapProfileEnd.
  • Re‑enables assets before starting a profile, waits for heapProfile + heapProfileEnd to succeed, then disables assets only after completion.
  • Verifies errors again with assets disabled, then re‑enables and runs a final successful profile.
    Using common.platformTimeout(100) between config changes and operations is consistent with existing tests and provides reasonable slack for agent reconfiguration. I don’t see correctness or flakiness risks beyond the usual timeout tuning.
agents/grpc/src/proto/reconfigure.pb.h (1)

219-235: Generated assetsEnabled field wiring in ReconfigureBody is consistent

The additions for assetsEnabled (field number 13) in ReconfigureBody—enum constant, public/ internal accessors, has‑bit mask 0x00000800U, and the new assetsenabled_ member in Impl_—are all consistent with the existing pattern for other optional bools like contCpuProfile. The parse table template and inline methods look mechanically generated and match the corresponding .cc layout.

Also applies to: 392-447, 1175-1201

agents/grpc/src/proto/reconfigure.pb.cc (1)

29-51: Protobuf implementation for assetsEnabled is consistent with the header and proto

The .cc updates for ReconfigureBody.assetsEnabled—Impl_ initialization, offsets/has‑bit layout, descriptor protodef string, parse table entry for field 13, and the plumbing in Clear, _InternalSerialize, ByteSizeLong, MergeImpl, and InternalSwap—all align with the new header definitions and appear mechanically generated. I don’t see any inconsistencies in offsets, masks, or serialization logic.

Also applies to: 103-147, 153-173, 329-439, 440-472, 581-587, 611-625, 663-717, 728-745

santigimeno added a commit that referenced this pull request Dec 2, 2025
Add comprehensive runtime control for asset collection (CPU/heap
profiling, snapshots) with multiple configuration methods.

Implements assetsEnabled flag with full control surface:
- NSOLID_ASSETS_ENABLED environment variable
- nsolid.start({ assetsEnabled: true/false }) configuration
- Runtime helpers: enableAssets() and disableAssets()
- Dynamic updates via gRPC reconfigure protocol
- Validation in both gRPC and ZMQ agents with proper errors
- Continuous profiler integration respects assetsEnabled state

This allows operators to disable expensive profiling operations
at runtime without process restarts, improving resource management
and production safety.

Also adds missing enableTraces()/disableTraces() helper methods
to complete the tracing API surface.

Comprehensive test coverage added for all asset types across gRPC,
ZMQ, and parallel test suites.

PR-URL: #377
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
@santigimeno
Copy link
Copy Markdown
Member Author

Landed in 75faeaa

@santigimeno santigimeno closed this Dec 2, 2025
@santigimeno santigimeno deleted the santi/assets_enabled branch December 3, 2025 00:53
santigimeno added a commit that referenced this pull request Dec 3, 2025
Add comprehensive runtime control for asset collection (CPU/heap
profiling, snapshots) with multiple configuration methods.

Implements assetsEnabled flag with full control surface:
- NSOLID_ASSETS_ENABLED environment variable
- nsolid.start({ assetsEnabled: true/false }) configuration
- Runtime helpers: enableAssets() and disableAssets()
- Dynamic updates via gRPC reconfigure protocol
- Validation in both gRPC and ZMQ agents with proper errors
- Continuous profiler integration respects assetsEnabled state

This allows operators to disable expensive profiling operations
at runtime without process restarts, improving resource management
and production safety.

Also adds missing enableTraces()/disableTraces() helper methods
to complete the tracing API surface.

Comprehensive test coverage added for all asset types across gRPC,
ZMQ, and parallel test suites.

PR-URL: #377
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
santigimeno added a commit that referenced this pull request Dec 3, 2025
Add comprehensive runtime control for asset collection (CPU/heap
profiling, snapshots) with multiple configuration methods.

Implements assetsEnabled flag with full control surface:
- NSOLID_ASSETS_ENABLED environment variable
- nsolid.start({ assetsEnabled: true/false }) configuration
- Runtime helpers: enableAssets() and disableAssets()
- Dynamic updates via gRPC reconfigure protocol
- Validation in both gRPC and ZMQ agents with proper errors
- Continuous profiler integration respects assetsEnabled state

This allows operators to disable expensive profiling operations
at runtime without process restarts, improving resource management
and production safety.

Also adds missing enableTraces()/disableTraces() helper methods
to complete the tracing API surface.

Comprehensive test coverage added for all asset types across gRPC,
ZMQ, and parallel test suites.

PR-URL: #377
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants