Skip to content

Conversation

@loganintech
Copy link
Contributor

@loganintech loganintech commented Oct 6, 2025

Summary by CodeRabbit

  • New Features

    • Optional CPU/memory profiling with timestamped output, CLI flags (--profile-cpu, --profile-mem, --profile-dir, --profile-parent), parent/child profiling, and runtime lifecycle management.
    • Remote flush endpoint to persist profiles on demand (profiles can be flushed via a service/API).
    • Profiling helper utilities to start/stop/write profiles; runner ensures flush-on-exit.
  • Bug Fixes / Validation

    • Enhanced validation and structured multi-error reporting for profile-related configs and flush messages.

@coderabbitai
Copy link

coderabbitai bot commented Oct 6, 2025

Walkthrough

Adds end-to-end profiling: proto messages/service (ProfileConfig, ProfileService + FlushProfiles), generated validators, a profiling package, CLI flags and default fields, runner and connector wiring to start/stop/write profiles, and a gRPC endpoint to flush profiles.

Changes

Cohort / File(s) Summary
Proto: ProfileConfig + Service
proto/c1/connector_wrapper/v1/connector_wrapper.proto
Adds ProfileConfig message, embeds it in ServerConfig, and introduces ProfileService with FlushProfiles RPC plus FlushProfilesRequest/FlushProfilesResponse.
Generated validation
pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go
Adds Validate/ValidateAll for ProfileConfig, FlushProfilesRequest, FlushProfilesResponse; multi-error wrappers and structured validation error types; wires ProfileConfig validation into ServerConfig validation.
Profiling runtime
pkg/profiling/profiling.go
New Profiler type with New / NewWithPrefix, and lifecycle methods Start, Stop, WriteMemProfile to manage CPU and memory profiling files and logging.
Connector core & service
internal/connector/connector.go
internal/connector/profile_service.go
Adds profileCfg field and WithProfileConfig option; includes ProfileConfig in server config; initializes profiler lifecycle in run flow; registers ProfileService implementing FlushProfiles.
Runner: parent & child profiling
pkg/connectorrunner/runner.go
Adds WithProfileConfig and WithParentProfileConfig options, runner fields for profile configs and parentProfiler; wires start/stop/write and calls FlushProfiles on one-shot exit.
CLI wiring & flags
pkg/cli/commands.go
Adds CLI flags profile-dir, profile-cpu, profile-mem, profile-parent; constructs ProfileConfig(s) for child and optional parent and attaches them to runner/child; updates import alias usage.
CLI defaults / fields
pkg/field/defaults.go
Adds default fields profile-dir, profile-cpu, profile-mem, profile-parent and includes them in DefaultFields.
Types: public API
pkg/types/types.go
Extends ClientWrapper interface with FlushProfiles(ctx context.Context) error.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User/CLI
  participant CLI as CLI
  participant R as ConnectorRunner
  participant CW as ConnectorWrapper
  participant S as SubprocessServer
  participant P as Profiler
  participant PS as ProfileService (gRPC)

  U->>CLI: run with profile flags
  CLI->>CLI: build ProfileConfig(s) (child / optional parent)
  CLI->>R: Run(..., WithProfileConfig, WithParentProfileConfig?)
  R->>CW: New(..., WithProfileConfig)
  CW->>S: Start subprocess (ServerConfig includes ProfileConfig)

  alt child profiling enabled
    CW->>P: New(cfg) / Start()
    CW->>S: register ProfileService(profiler)
  end

  alt parent profiling enabled
    R->>P: NewWithPrefix(parentCfg,"parent") / Start()
  end

  CW->>S: Serve()
  S-->>CW: Serve terminates

  alt shutdown / one-shot exit
    CW->>P: Stop()
    CW->>P: WriteMemProfile()
    R->>PS: FlushProfiles RPC (if invoked)
    PS-->>R: success/failure
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • ggreer
  • kans

Poem

I twitch my whiskers, files aglow,
I spin the clock where samples flow.
CPU crumbs and mem‑heap song,
I stop, I write—the traces long.
A rabbit hops — the profiles hum. 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Add CPU profile capture” correctly highlights the addition of CPU profiling support included in this changeset and is concise and clear, though the PR also adds memory profiling and related profiling infrastructure. It is not misleading or generic and refers to a real aspect of the main feature.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ 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 logan/add-cpu-profile-capture

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efe0f22 and 3d33313.

📒 Files selected for processing (1)
  • internal/connector/profile_service.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/connector/profile_service.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go (3)
  • UnimplementedProfileServiceServer (66-66)
  • UnimplementedProfileServiceServer (68-70)
  • UnimplementedProfileServiceServer (71-71)
pkg/profiling/profiling.go (1)
  • Profiler (18-23)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • FlushProfilesRequest (167-171)
  • FlushProfilesRequest (184-184)
  • FlushProfilesRequest (199-201)
  • FlushProfilesResponse (203-209)
  • FlushProfilesResponse (222-222)
  • FlushProfilesResponse (237-239)
⏰ 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). (1)
  • GitHub Check: go-test (1.25.2, windows-latest)

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

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f48076 and e816c32.

⛔ Files ignored due to path filters (1)
  • pb/c1/connector_wrapper/v1/connector_wrapper.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (7)
  • internal/connector/connector.go (6 hunks)
  • pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go (2 hunks)
  • pkg/cli/commands.go (2 hunks)
  • pkg/connectorrunner/runner.go (4 hunks)
  • pkg/field/defaults.go (2 hunks)
  • pkg/profiling/profiling.go (1 hunks)
  • proto/c1/connector_wrapper/v1/connector_wrapper.proto (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-02T21:28:25.367Z
Learnt from: MarcusGoldschmidt
PR: ConductorOne/baton-sdk#393
File: pkg/test/integration_wrapper.go:54-54
Timestamp: 2025-06-02T21:28:25.367Z
Learning: The `connectorbuilder.NewConnector` function in the baton-sdk already validates the connector parameter using a type switch and returns appropriate errors for nil or invalid connectors in the default case, so additional nil checks are not needed before calling this function.

Applied to files:

  • pkg/connectorrunner/runner.go
  • pkg/cli/commands.go
📚 Learning: 2025-06-02T21:28:25.367Z
Learnt from: MarcusGoldschmidt
PR: ConductorOne/baton-sdk#393
File: pkg/test/integration_wrapper.go:54-54
Timestamp: 2025-06-02T21:28:25.367Z
Learning: The `connectorbuilder.NewConnector` function in the baton-sdk already validates the connector parameter and returns appropriate errors for nil or invalid connectors, so additional nil checks are not needed before calling this function.

Applied to files:

  • pkg/connectorrunner/runner.go
  • pkg/cli/commands.go
🧬 Code graph analysis (6)
pkg/field/defaults.go (2)
pkg/field/fields.go (2)
  • StringField (198-214)
  • BoolField (176-196)
pkg/field/field_options.go (4)
  • WithDescription (47-53)
  • WithPersistent (129-135)
  • WithExportTarget (103-111)
  • ExportTargetNone (97-97)
pkg/connectorrunner/runner.go (2)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
internal/connector/connector.go (2)
  • WithProfileConfig (130-135)
  • Option (78-78)
pkg/profiling/profiling.go (1)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
pkg/cli/commands.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
internal/connector/connector.go (1)
  • WithProfileConfig (130-135)
pkg/connectorrunner/runner.go (1)
  • WithProfileConfig (654-659)
internal/connector/connector.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
pkg/connectorrunner/runner.go (2)
  • WithProfileConfig (654-659)
  • Option (255-255)
pkg/profiling/profiling.go (1)
  • New (27-49)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go (1)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
🪛 GitHub Actions: ci
pkg/cli/commands.go

[error] 25-25: golangci-lint: string literal not terminated (typecheck).

🪛 GitHub Check: go-lint
pkg/cli/commands.go

[failure] 25-25:
string literal not terminated (typecheck)

Comment on lines 24 to 25
connectorwrapperV1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1"
v1 "github.com/conductorone/baton-sdk/pb/c1/connector_w
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix duplicate proto import to restore build.

The import block now pulls in github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1 twice under different aliases (connectorwrapperV1 and v1). Go forbids duplicate imports, which is why golangci-lint is failing with the “string literal not terminated” typecheck error. Drop the second import and reuse a single alias (e.g. stick with v1) for both ProfileConfig and ServerConfig.

-import (
-
-	connectorwrapperV1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1"
-	v1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1"
+import (
+
+	v1 "github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1"

and later

-			profileCfg := &connectorwrapperV1.ProfileConfig{
+			profileCfg := &v1.ProfileConfig{
 				OutputDir: profileDir,
 				EnableCpu: enableCPU,
 				EnableMem: enableMem,
 			}

Apply the same alias where ProfileConfig is referenced elsewhere. Once the duplicate import is gone, the build should succeed again.

Also applies to: 303-321

🧰 Tools
🪛 GitHub Actions: ci

[error] 25-25: golangci-lint: string literal not terminated (typecheck).

🪛 GitHub Check: go-lint

[failure] 25-25:
string literal not terminated (typecheck)

🤖 Prompt for AI Agents
In pkg/cli/commands.go around lines 24-25 (and similarly lines 303-321), there
is a duplicate import of
github.com/conductorone/baton-sdk/pb/c1/connector_wrapper/v1 under two aliases
(connectorwrapperV1 and v1); remove the duplicate import and keep a single alias
(use v1), then update any places that reference connectorwrapperV1.ProfileConfig
(or connectorwrapperV1.ServerConfig) to use v1.ProfileConfig (and
v1.ServerConfig) so all references use the same import alias and the
build/typecheck error is resolved.

Comment on lines 109 to 120
f, err := os.Create(p.memFilePath)
if err != nil {
l.Error("failed to create memory profile file", zap.Error(err))
return err
}
defer f.Close()

// Write heap profile without forcing GC - we want to see actual memory state
if err := pprof.WriteHeapProfile(f); err != nil {
l.Error("failed to write memory profile", zap.Error(err))
return err
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure the mem profile directory exists before creating the file

When only memory profiling is enabled (CPU disabled), outputDir is never created. os.Create on Line 109 will fail with ENOENT, so mem profiles are silently skipped. Mirror the CPU path and call os.MkdirAll(filepath.Dir(p.memFilePath), 0o755) before creating the file.

+	outputDir := filepath.Dir(p.memFilePath)
+	if err := os.MkdirAll(outputDir, 0o755); err != nil {
+		l.Error("failed to create profile output directory", zap.Error(err))
+		return err
+	}
+
 	f, err := os.Create(p.memFilePath)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
f, err := os.Create(p.memFilePath)
if err != nil {
l.Error("failed to create memory profile file", zap.Error(err))
return err
}
defer f.Close()
// Write heap profile without forcing GC - we want to see actual memory state
if err := pprof.WriteHeapProfile(f); err != nil {
l.Error("failed to write memory profile", zap.Error(err))
return err
}
// Ensure the mem‐profile output directory exists
outputDir := filepath.Dir(p.memFilePath)
if err := os.MkdirAll(outputDir, 0o755); err != nil {
l.Error("failed to create profile output directory", zap.Error(err))
return err
}
f, err := os.Create(p.memFilePath)
if err != nil {
l.Error("failed to create memory profile file", zap.Error(err))
return err
}
defer f.Close()
// Write heap profile without forcing GC - we want to see actual memory state
if err := pprof.WriteHeapProfile(f); err != nil {
l.Error("failed to write memory profile", zap.Error(err))
return err
}
🤖 Prompt for AI Agents
In pkg/profiling/profiling.go around lines 109 to 120, the code calls
os.Create(p.memFilePath) without ensuring the parent directory exists which
causes os.Create to fail with ENOENT when only memory profiling is enabled;
before creating the file call os.MkdirAll(filepath.Dir(p.memFilePath), 0o755)
and check its error, log and return on failure, then proceed to create the file
and write the heap profile as before.

}

// New creates a new Profiler from the given configuration.
// Filenames are generated with a timestamp: cpu-YYYYMMDD-HHMMSS.prof and mem-YYYYMMDD-HHMMSS.prof
Copy link
Contributor

Choose a reason for hiding this comment

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

pprof?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This uses pprof, I don't really understand what your question is here. Do you mean the file extension should be pprof instead of prof?

Copy link

@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)
pkg/profiling/profiling.go (1)

109-119: Ensure the memory profile directory exists before creating the file.

When only memory profiling is enabled (CPU profiling disabled), the output directory is never created, causing os.Create to fail with ENOENT. This issue was previously identified.

Apply this diff to create the directory before creating the file:

+	outputDir := filepath.Dir(p.memFilePath)
+	if err := os.MkdirAll(outputDir, 0o755); err != nil {
+		l.Error("failed to create profile output directory", zap.Error(err))
+		return err
+	}
+
 	f, err := os.Create(p.memFilePath)
🧹 Nitpick comments (1)
pkg/profiling/profiling.go (1)

26-26: Add period to godoc comment.

The godoc comment should end with a period to conform to Go documentation conventions.

Apply this diff:

-// Filenames are generated with a timestamp: cpu-YYYYMMDD-HHMMSS.prof and mem-YYYYMMDD-HHMMSS.prof
+// Filenames are generated with a timestamp: cpu-YYYYMMDD-HHMMSS.prof and mem-YYYYMMDD-HHMMSS.prof.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e816c32 and 17981bd.

⛔ Files ignored due to path filters (2)
  • pb/c1/connector_wrapper/v1/connector_wrapper.pb.go is excluded by !**/*.pb.go
  • pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (8)
  • internal/connector/connector.go (7 hunks)
  • internal/connector/profile_service.go (1 hunks)
  • pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go (3 hunks)
  • pkg/cli/commands.go (3 hunks)
  • pkg/connectorrunner/runner.go (6 hunks)
  • pkg/profiling/profiling.go (1 hunks)
  • pkg/types/types.go (1 hunks)
  • proto/c1/connector_wrapper/v1/connector_wrapper.proto (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-02T21:28:25.367Z
Learnt from: MarcusGoldschmidt
PR: ConductorOne/baton-sdk#393
File: pkg/test/integration_wrapper.go:54-54
Timestamp: 2025-06-02T21:28:25.367Z
Learning: The `connectorbuilder.NewConnector` function in the baton-sdk already validates the connector parameter and returns appropriate errors for nil or invalid connectors, so additional nil checks are not needed before calling this function.

Applied to files:

  • pkg/cli/commands.go
  • pkg/connectorrunner/runner.go
📚 Learning: 2025-06-02T21:28:25.367Z
Learnt from: MarcusGoldschmidt
PR: ConductorOne/baton-sdk#393
File: pkg/test/integration_wrapper.go:54-54
Timestamp: 2025-06-02T21:28:25.367Z
Learning: The `connectorbuilder.NewConnector` function in the baton-sdk already validates the connector parameter using a type switch and returns appropriate errors for nil or invalid connectors in the default case, so additional nil checks are not needed before calling this function.

Applied to files:

  • pkg/cli/commands.go
  • pkg/connectorrunner/runner.go
🧬 Code graph analysis (6)
internal/connector/connector.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
  • FlushProfilesRequest (158-162)
  • FlushProfilesRequest (175-175)
  • FlushProfilesRequest (190-192)
pkg/profiling/profiling.go (1)
  • New (27-49)
pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go (2)
  • RegisterProfileServiceServer (80-89)
  • NewProfileServiceClient (38-40)
pkg/cli/commands.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
  • ServerConfig (90-98)
  • ServerConfig (111-111)
  • ServerConfig (126-128)
internal/connector/connector.go (1)
  • WithProfileConfig (130-135)
pkg/connectorrunner/runner.go (1)
  • WithProfileConfig (662-667)
pkg/profiling/profiling.go (1)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
pkg/connectorrunner/runner.go (2)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (3)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
internal/connector/connector.go (2)
  • WithProfileConfig (130-135)
  • Option (78-78)
internal/connector/profile_service.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go (3)
  • UnimplementedProfileServiceServer (66-66)
  • UnimplementedProfileServiceServer (68-70)
  • UnimplementedProfileServiceServer (71-71)
pkg/profiling/profiling.go (1)
  • Profiler (18-23)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • FlushProfilesRequest (158-162)
  • FlushProfilesRequest (175-175)
  • FlushProfilesRequest (190-192)
  • FlushProfilesResponse (194-200)
  • FlushProfilesResponse (213-213)
  • FlushProfilesResponse (228-230)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go (1)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (9)
  • ProfileConfig (26-37)
  • ProfileConfig (50-50)
  • ProfileConfig (65-67)
  • FlushProfilesRequest (158-162)
  • FlushProfilesRequest (175-175)
  • FlushProfilesRequest (190-192)
  • FlushProfilesResponse (194-200)
  • FlushProfilesResponse (213-213)
  • FlushProfilesResponse (228-230)
🪛 GitHub Check: go-lint
pkg/profiling/profiling.go

[failure] 26-26:
Comment should end in a period (godot)

internal/connector/profile_service.go

[failure] 34-34:
error is not nil (line 33) but it returns nil (nilerr)


[failure] 26-26:
error is not nil (line 25) but it returns nil (nilerr)

⏰ 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). (1)
  • GitHub Check: go-test (1.25.x, windows-latest)
🔇 Additional comments (11)
pkg/connectorrunner/runner.go (6)

30-30: LGTM!

The import alias for the connector wrapper v1 package is correctly added and follows the naming conventions used elsewhere in the codebase.


137-139: LGTM!

The flushProfiles method correctly delegates to the client wrapper's FlushProfiles method and properly propagates errors.


192-195: LGTM!

The profile flushing before exit is correctly implemented with appropriate error handling. Logging the error as a warning and continuing is the right approach for best-effort cleanup during shutdown.


356-356: LGTM!

The profileConfig field is correctly added to the runnerConfig struct with the appropriate type.


662-667: LGTM!

The WithProfileConfig option function follows the established pattern for other option functions and correctly stores the profile configuration.


704-706: LGTM!

The profile configuration is correctly passed to the connector wrapper when provided, following the same pattern as other conditional wrapper options.

pkg/profiling/profiling.go (4)

1-15: LGTM!

The package declaration and imports are appropriate. All imported packages are used in the implementation.


17-23: LGTM!

The Profiler struct is well-designed with appropriate fields to manage CPU and memory profiling state.


51-79: LGTM!

The Start method correctly initializes CPU profiling with proper directory creation, error handling, and logging. The cleanup on error (closing the file if pprof.StartCPUProfile fails) is appropriate.


81-98: LGTM!

The Stop method correctly stops CPU profiling, closes the file, and handles errors appropriately. Nullifying cpuFile after closing prevents double-close issues.

pb/c1/connector_wrapper/v1/connector_wrapper.pb.validate.go (1)

38-142: LGTM!

The generated validation code correctly implements the protoc-gen-validate pattern for ProfileConfig, FlushProfilesRequest, and FlushProfilesResponse. The ServerConfig validation has been properly extended to validate the embedded ProfileConfig. All error types and validation methods follow the standard generated structure.

Also applies to: 226-253, 332-538

Comment on lines 24 to 38
// Stop CPU profiling to flush data
if err := ps.profiler.Stop(ctx); err != nil {
return &connectorwrapperV1.FlushProfilesResponse{
Success: false,
Error: err.Error(),
}, nil
}

// Write memory profile
if err := ps.profiler.WriteMemProfile(ctx); err != nil {
return &connectorwrapperV1.FlushProfilesResponse{
Success: false,
Error: err.Error(),
}, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Return a gRPC error instead of success=false with nil error.

FlushProfiles currently swallows the actual error and replies with success=false while returning nil for the RPC error. Apart from tripping nilerr in CI, this hides the failure from gRPC clients that only check the RPC error. Please propagate the failure as a status.Error (e.g., codes.Internal) and let callers handle it via the RPC error path.

-	if err := ps.profiler.Stop(ctx); err != nil {
-		return &connectorwrapperV1.FlushProfilesResponse{
-			Success: false,
-			Error:   err.Error(),
-		}, nil
-	}
+	if err := ps.profiler.Stop(ctx); err != nil {
+		return nil, status.Errorf(codes.Internal, "failed to stop profiler: %v", err)
+	}
@@
-	if err := ps.profiler.WriteMemProfile(ctx); err != nil {
-		return &connectorwrapperV1.FlushProfilesResponse{
-			Success: false,
-			Error:   err.Error(),
-		}, nil
-	}
+	if err := ps.profiler.WriteMemProfile(ctx); err != nil {
+		return nil, status.Errorf(codes.Internal, "failed to write memory profile: %v", err)
+	}

Remember to add the necessary status/codes imports. After this change the success flag can stay true when no error occurs, and the lint failure should disappear. Based on static analysis hints

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Stop CPU profiling to flush data
if err := ps.profiler.Stop(ctx); err != nil {
return &connectorwrapperV1.FlushProfilesResponse{
Success: false,
Error: err.Error(),
}, nil
}
// Write memory profile
if err := ps.profiler.WriteMemProfile(ctx); err != nil {
return &connectorwrapperV1.FlushProfilesResponse{
Success: false,
Error: err.Error(),
}, nil
}
// Stop CPU profiling to flush data
if err := ps.profiler.Stop(ctx); err != nil {
return nil, status.Errorf(codes.Internal, "failed to stop profiler: %v", err)
}
// Write memory profile
if err := ps.profiler.WriteMemProfile(ctx); err != nil {
return nil, status.Errorf(codes.Internal, "failed to write memory profile: %v", err)
}
🧰 Tools
🪛 GitHub Check: go-lint

[failure] 34-34:
error is not nil (line 33) but it returns nil (nilerr)


[failure] 26-26:
error is not nil (line 25) but it returns nil (nilerr)

🤖 Prompt for AI Agents
internal/connector/profile_service.go lines 24-38: the RPC currently returns a
FlushProfilesResponse with Success=false and a nil gRPC error when profiler.Stop
or profiler.WriteMemProfile fail, which hides the failure from gRPC clients;
change these branches to return (nil, status.Error(codes.Internal, err.Error()))
(importing "google.golang.org/grpc/status" and "google.golang.org/grpc/codes"),
and keep returning the successful response only when no error occurs so callers
receive a proper gRPC error instead of a nil error with success=false.

Copy link

@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)
internal/connector/profile_service.go (1)

29-43: Return proper gRPC status errors instead of success=false.

The past review comment correctly identifies this issue. The current implementation returns success=false with a nil gRPC error, which violates gRPC conventions and triggers the nilerr lint failure. Please follow the suggested fix to return status.Errorf(codes.Internal, ...) for both error cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4376171 and 7a45bc0.

📒 Files selected for processing (2)
  • internal/connector/connector.go (7 hunks)
  • internal/connector/profile_service.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
internal/connector/connector.go (4)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • ProfileConfig (26-39)
  • ProfileConfig (52-52)
  • ProfileConfig (67-69)
  • FlushProfilesRequest (167-171)
  • FlushProfilesRequest (184-184)
  • FlushProfilesRequest (199-201)
pkg/connectorrunner/runner.go (2)
  • WithProfileConfig (680-685)
  • Option (280-280)
pkg/profiling/profiling.go (1)
  • New (28-36)
pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go (2)
  • RegisterProfileServiceServer (80-89)
  • NewProfileServiceClient (38-40)
internal/connector/profile_service.go (3)
pb/c1/connector_wrapper/v1/connector_wrapper_grpc.pb.go (3)
  • UnimplementedProfileServiceServer (66-66)
  • UnimplementedProfileServiceServer (68-70)
  • UnimplementedProfileServiceServer (71-71)
pkg/profiling/profiling.go (1)
  • Profiler (18-23)
pb/c1/connector_wrapper/v1/connector_wrapper.pb.go (6)
  • FlushProfilesRequest (167-171)
  • FlushProfilesRequest (184-184)
  • FlushProfilesRequest (199-201)
  • FlushProfilesResponse (203-209)
  • FlushProfilesResponse (222-222)
  • FlushProfilesResponse (237-239)
🪛 GitHub Check: go-lint
internal/connector/profile_service.go

[failure] 39-39:
error is not nil (line 38) but it returns nil (nilerr)


[failure] 31-31:
error is not nil (line 30) but it returns nil (nilerr)

⏰ 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). (1)
  • GitHub Check: go-test (1.25.x, windows-latest)
🔇 Additional comments (4)
internal/connector/connector.go (4)

130-135: LGTM!

The option follows the established pattern and correctly sets the profile configuration.


162-171: LGTM!

The profiling initialization is correctly placed before the gRPC server startup to capture the full lifecycle, with appropriate error handling and logging.


205-209: LGTM!

The ProfileService registration follows the same conditional pattern as other feature-gated services in this file.


238-238: LGTM!

The ProfileConfig is correctly propagated to the subprocess server configuration.

Comment on lines +369 to +390
// FlushProfiles calls the ProfileService RPC to flush any active profiling data.
func (cw *wrapper) FlushProfiles(ctx context.Context) error {
cw.mtx.RLock()
conn := cw.conn
cw.mtx.RUnlock()

if conn == nil {
return fmt.Errorf("no active connection")
}

client := connectorwrapperV1.NewProfileServiceClient(conn)
resp, err := client.FlushProfiles(ctx, &connectorwrapperV1.FlushProfilesRequest{})
if err != nil {
return err
}

if !resp.Success {
return fmt.Errorf("profile flush failed: %s", resp.Error)
}

return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Update for compatibility with the profile_service.go gRPC error fix.

Once profile_service.go is fixed to return proper gRPC errors (as suggested in the previous review), this method will break. When the service returns an error via status.Errorf, resp will be nil and lines 385-387 will cause a panic or become unreachable.

Apply this diff to handle errors correctly:

 func (cw *wrapper) FlushProfiles(ctx context.Context) error {
 	cw.mtx.RLock()
 	conn := cw.conn
 	cw.mtx.RUnlock()
 
 	if conn == nil {
 		return fmt.Errorf("no active connection")
 	}
 
 	client := connectorwrapperV1.NewProfileServiceClient(conn)
-	resp, err := client.FlushProfiles(ctx, &connectorwrapperV1.FlushProfilesRequest{})
-	if err != nil {
-		return err
-	}
-
-	if !resp.Success {
-		return fmt.Errorf("profile flush failed: %s", resp.Error)
-	}
-
-	return nil
+	_, err := client.FlushProfiles(ctx, &connectorwrapperV1.FlushProfilesRequest{})
+	return err
 }

This simplifies the method to rely on the gRPC error convention where errors are returned via the error return value, not via response fields.

🤖 Prompt for AI Agents
In internal/connector/connector.go around lines 369-390, FlushProfiles currently
assumes a non-nil resp and checks resp.Success/resp.Error which will break once
the service returns gRPC errors via status.Errorf (resp will be nil). Change the
function to follow gRPC conventions: call client.FlushProfiles, if err != nil
return err, otherwise return nil (remove reliance on resp.Success/resp.Error);
keep the early conn nil check and mutex usage as-is and ensure no
nil-dereference of resp occurs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants