Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profiler server for pprof #591

Merged
merged 4 commits into from
Sep 26, 2024
Merged

Add profiler server for pprof #591

merged 4 commits into from
Sep 26, 2024

Conversation

peterargue
Copy link
Contributor

@peterargue peterargue commented Sep 25, 2024

Closes: #???

Description

Add a profiler to the gateway


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

Summary by CodeRabbit

  • New Features

    • Introduced profiling capabilities with new configuration flags: profiler-enabled, profiler-host, and profiler-port.
    • Added instructions in the README for enabling and using the profiling feature.
  • Documentation

    • Updated README for clarity on new configuration flags and profiling instructions.
  • Bug Fixes

    • Improved command readability in the Makefile for starting the local environment.

Copy link
Contributor

coderabbitai bot commented Sep 25, 2024

Walkthrough

The changes introduce profiling capabilities to the application by adding new configuration flags and a profiling server. The Makefile command for starting the application has been updated for clarity and to include profiling options. The README.md has been modified to document the new flags. A new ProfileServer struct has been created in api/profiler.go, and the Bootstrap struct in bootstrap.go has been updated to manage the profiler server. Additionally, the configuration structure in config/config.go has been extended to include fields for the profiler settings.

Changes

File Change Summary
Makefile Updated start-local command for readability; added --profiler-enabled=true and --profiler-port=6060.
README.md Added documentation for new flags: profiler-enabled, profiler-host, profiler-port; reformatted section.
api/profiler.go Introduced ProfileServer struct with methods for managing a profiling server; includes initialization and shutdown logic.
bootstrap/bootstrap.go Added profiler field to Bootstrap struct; implemented StartProfilerServer and StopProfilerServer methods.
config/config.go Added fields for profiler configuration: ProfilerEnabled, ProfilerHost, ProfilerPort; updated FromFlags function for command-line flag support.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Bootstrap
    participant ProfileServer

    User->>Bootstrap: Start Application
    Bootstrap->>ProfileServer: StartProfilerServer()
    ProfileServer-->>Bootstrap: Server Started
    Bootstrap->>User: Application Running
    User->>Bootstrap: Stop Application
    Bootstrap->>ProfileServer: StopProfilerServer()
    ProfileServer-->>Bootstrap: Server Stopped
Loading

Poem

🐇 In the meadow where rabbits play,
New flags and features brighten the day.
A profiler hops in, all spry and keen,
Capturing data, a sight to be seen!
With each little change, we dance and cheer,
For a faster, clearer path, we hold dear! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (7)
api/profiler.go (1)

1-60: Enhance code documentation with comments.

The implementation of the ProfileServer and its methods is generally good. However, the code would benefit from additional comments explaining the purpose of the ProfileServer struct and its methods.

Consider adding comments to:

  1. The ProfileServer struct, explaining its purpose and usage.
  2. Each method, briefly describing what it does and any important details.

For example:

// ProfileServer manages an HTTP server for profiling purposes.
type ProfileServer struct {
    // ...
}

// NewProfileServer creates a new ProfileServer instance with the given logger, host, and port.
func NewProfileServer(
    // ...
) *ProfileServer {
    // ...
}

// Start launches the profiling server in a non-blocking manner.
func (s *ProfileServer) Start() {
    // ...
}

// Stop gracefully shuts down the profiling server with a timeout.
func (s *ProfileServer) Stop() error {
    // ...
}

These comments will improve the code's readability and make it easier for other developers to understand and use the ProfileServer.

config/config.go (3)

100-105: LGTM! Consider grouping profiler-related fields.

The new fields for profiler configuration are well-defined and appropriately commented. They provide a clear way to enable and configure the profiler server.

Consider grouping these profiler-related fields together under a nested struct for better organization, especially if more profiler-related configurations might be added in the future. For example:

type ProfilerConfig struct {
    Enabled bool
    Host    string
    Port    int
}

type Config struct {
    // ... other fields ...
    Profiler ProfilerConfig
}

This structure would improve readability and make it easier to add more profiler-related configurations in the future.


168-170: LGTM! Consider using constants for default values.

The new flags for profiler configuration are well-implemented and correspond correctly to the new fields in the Config struct. The default values and descriptions are appropriate.

For consistency and ease of maintenance, consider defining constants for the default values of profiler-host and profiler-port. This approach would make it easier to update these values in the future if needed. For example:

const (
    DefaultProfilerHost = "localhost"
    DefaultProfilerPort = 6060
)

// Then in FromFlags():
flag.StringVar(&cfg.ProfilerHost, "profiler-host", DefaultProfilerHost, "Host for the Profiler server")
flag.IntVar(&cfg.ProfilerPort, "profiler-port", DefaultProfilerPort, "Port for the Profiler server")

This change would improve consistency with other parts of the codebase that might use these default values and make it easier to update them in the future.


Line range hint 1-170: Summary: Profiler configuration successfully implemented

The changes in this PR successfully introduce profiler configuration to the gateway application, aligning with the stated objective of enhancing performance monitoring and analysis. The implementation is clean, well-integrated, and follows existing patterns in the codebase.

Key points:

  1. New fields added to the Config struct for profiler settings.
  2. Corresponding flags added to the FromFlags function for command-line configuration.
  3. Default values are sensible, with the profiler disabled by default.

The implementation allows for easy enabling and configuration of the profiler server, which should facilitate better performance insights as intended.

As the gateway application grows, consider creating a separate package for profiling-related functionality. This could include the profiler server implementation and any associated utilities. This separation of concerns would keep the main configuration file focused on settings and improve overall code organization.

README.md (3)

175-211: LGTM! Configuration flags table updated with profiler options.

The new profiler-related flags have been added correctly, and the table formatting has been improved for better readability. The descriptions are clear and informative.

Consider adding a brief note about the security implications of enabling the profiler in production environments, as it may expose sensitive information.

🧰 Tools
Markdownlint

183-183: null
Spaces inside code span elements

(MD038, no-space-in-code)


258-276: Great addition of debugging instructions with profiler usage.

The new "Debugging" section provides clear instructions on how to enable and use the profiler, which is valuable for developers working on or with the EVM Gateway.

Consider the following improvements:

  1. Add a note about the performance impact of enabling the profiler in production environments.
  2. Mention that the profiler should be used cautiously in production due to potential security risks.
  3. Add examples of what kind of insights developers can gain from the profiler output.
  4. Include a brief explanation of when and why a developer might need to use the profiler.

Here's a suggested addition to the beginning of the Profiler section:

The EVM Gateway supports profiling via the `pprof` package, which is useful for identifying performance bottlenecks and memory leaks during development and testing. Note that enabling the profiler in production environments may have performance implications and could potentially expose sensitive information. Use with caution.
🧰 Tools
Markdownlint

263-263: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


270-270: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


273-273: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


183-183: Minor markdown formatting improvements

There are a few minor markdown formatting issues that could be addressed to improve the document's consistency and readability:

  1. On line 183, remove the spaces inside the code span element: Change `testnet ` to `testnet`.

  2. For the fenced code blocks starting on lines 263, 270, and 273, specify the language for better syntax highlighting. For example:

    --profiler-enabled=true
    --profiler-host=localhost
    --profiler-port=6060
    go tool pprof -http :2000 http://localhost:6060/debug/pprof/profile
    curl --output trace.out http://localhost:6060/debug/pprof/trace
    go tool trace -http :2001 trace.out

These changes will improve the document's formatting and enable syntax highlighting for the code blocks.

Also applies to: 263-263, 270-270, 273-273

🧰 Tools
Markdownlint

183-183: null
Spaces inside code span elements

(MD038, no-space-in-code)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d5b8984 and 30cd0a7.

📒 Files selected for processing (5)
  • Makefile (1 hunks)
  • README.md (2 hunks)
  • api/profiler.go (1 hunks)
  • bootstrap/bootstrap.go (3 hunks)
  • config/config.go (2 hunks)
🧰 Additional context used
Gitleaks
Makefile

52-52: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


53-53: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Markdownlint
README.md

183-183: null
Spaces inside code span elements

(MD038, no-space-in-code)


263-263: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


270-270: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)


273-273: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments not posted (6)
api/profiler.go (5)

1-12: LGTM: Package declaration and imports are appropriate.

The package name api is suitable for this file's purpose. The imports are relevant to the implemented functionality.

Note: The blank import _ "net/http/pprof" is correctly used to register HTTP handlers for profiling without directly using the package in the code.


14-18: LGTM: ProfileServer struct is well-defined.

The ProfileServer struct contains appropriate fields for managing a profiling server:

  • logger for logging (using zerolog)
  • server for the HTTP server
  • endpoint to store the server's address

This structure provides a good foundation for the profiling functionality.


20-31: LGTM: NewProfileServer constructor is well-implemented.

The constructor function properly initializes a new ProfileServer instance:

  • It correctly uses net.JoinHostPort to create the endpoint string.
  • All fields of the struct are properly initialized.
  • The use of strconv.Itoa for port conversion is appropriate.

This implementation ensures that the ProfileServer is correctly set up with the provided parameters.


33-35: LGTM: ListenAddr method is correctly implemented.

This simple getter method returns the endpoint string, which is useful for reporting the server's address.


58-60: LGTM: Close method is correctly implemented.

The Close method directly closes the server, which is appropriate for immediate shutdown when graceful shutdown is not required.

Makefile (1)

48-58: Approve profiler addition and improved formatting

The changes to the start-local target look good. The command has been split across multiple lines, improving readability. The addition of --profiler-enabled=true and --profiler-port=6060 aligns with the PR objective of introducing a profiler to the gateway.

Consider securing sensitive information

The command includes sensitive information like API keys and addresses. Consider moving these to environment variables or a configuration file to enhance security.

You could refactor the command to use environment variables like this:

start-local:
	rm -rf db/
	rm -rf metrics/data/
	go run cmd/main/main.go \
		--flow-network-id=$(FLOW_NETWORK_ID) \
		--coinbase=$(COINBASE_ADDRESS) \
		--coa-address=$(COA_ADDRESS) \
		--coa-key=$(COA_KEY) \
		--wallet-api-key=$(WALLET_API_KEY) \
		--coa-resource-create=true \
		--gas-price=0 \
		--log-writer=console \
		--profiler-enabled=true \
		--profiler-port=6060

Then, you can set these environment variables in a .env file or through your CI/CD pipeline.

Verify profiler impact

While enabling the profiler is good for debugging and performance analysis, it's important to be aware of its impact on the application's performance.

Let's verify if there are any performance considerations mentioned in the codebase:

🧰 Tools
Gitleaks

52-52: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


53-53: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +51 to +56
func (s *ProfileServer) Stop() error {
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()

return s.server.Shutdown(ctx)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define shutdownTimeout constant.

The Stop method correctly uses a context with timeout for graceful shutdown. However, the shutdownTimeout constant is not defined in the provided code.

Please define the shutdownTimeout constant at the package level. For example:

const shutdownTimeout = 5 * time.Second

Adjust the duration as appropriate for your use case.

Comment on lines +37 to +49
func (s *ProfileServer) Start() {
go func() {
err := s.server.ListenAndServe()
if err != nil {
if errors.Is(err, http.ErrServerClosed) {
s.logger.Warn().Msg("Profiler server shutdown")
return
}
s.logger.Err(err).Msg("failed to start Profiler server")
panic(err)
}
}()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider error handling improvement in Start method.

The Start method correctly launches the server in a goroutine for non-blocking operation. However, panicking in case of a server start failure might not be the best approach in a production environment.

Consider logging the error and returning it to the caller instead of panicking. This allows the caller to decide how to handle the error. Here's a suggested modification:

func (s *ProfileServer) Start() error {
-	go func() {
+	errCh := make(chan error, 1)
+	go func() {
		err := s.server.ListenAndServe()
		if err != nil && !errors.Is(err, http.ErrServerClosed) {
			s.logger.Err(err).Msg("failed to start Profiler server")
-			panic(err)
+			errCh <- err
		}
+		close(errCh)
	}()
+	
+	// Wait a short time to catch immediate failures
+	select {
+	case err := <-errCh:
+		return err
+	case <-time.After(100 * time.Millisecond):
+		return nil
+	}
}

This change allows the method to return any immediate errors while still running the server in a goroutine.

Committable suggestion was skipped due to low confidence.

Comment on lines +52 to +53
--coa-key=2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 \
--wallet-api-key=2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 \
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address potential API key exposure

The static analysis tool has flagged potential API key exposure in these lines. This reinforces the need to move sensitive information out of the Makefile.

To address this:

  1. Move the API keys to environment variables as suggested earlier.
  2. Ensure that the .env file (if used) is added to .gitignore.
  3. Update the documentation to guide developers on setting up these environment variables locally.
🧰 Tools
Gitleaks

52-52: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


53-53: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

b.logger.Warn().Msg("Profiler server graceful shutdown timed out")
b.profiler.Close()
} else {
b.logger.Err(err).Msg("Profiler server graceful shutdown failed")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Specify log level when logging errors

In line 371, the code uses b.logger.Err(err) without specifying the log level. To ensure proper logging, chain the Error() method before Err(err).

Apply this diff to correct the log level:

-			b.logger.Err(err).Msg("Profiler server graceful shutdown failed")
+			b.logger.Error().Err(err).Msg("Profiler server graceful shutdown failed")
📝 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
b.logger.Err(err).Msg("Profiler server graceful shutdown failed")
b.logger.Error().Err(err).Msg("Profiler server graceful shutdown failed")

Comment on lines +352 to +355
b.profiler.Start()
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr())

return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential error from b.profiler.Start()

The method b.profiler.Start() may return an error, but the code does not check for it. To ensure robust error handling, consider checking for and returning any errors when starting the profiler server.

Apply this diff to handle the error:

     b.profiler = api.NewProfileServer(b.logger, b.config.ProfilerHost, b.config.ProfilerPort)

-    b.profiler.Start()
+    if err := b.profiler.Start(); err != nil {
+        return fmt.Errorf("failed to start profiler server: %w", err)
+    }
     b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr())
📝 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
b.profiler.Start()
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr())
return nil
if err := b.profiler.Start(); err != nil {
return fmt.Errorf("failed to start profiler server: %w", err)
}
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr())
return nil

Copy link
Contributor

@janezpodhostnik janezpodhostnik left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@m-Peter m-Peter left a comment

Choose a reason for hiding this comment

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

Awesome 💯

@peterargue peterargue merged commit 1d311ad into main Sep 26, 2024
2 checks passed
@peterargue peterargue deleted the petera/add-profiler branch September 26, 2024 13:38
@coderabbitai coderabbitai bot mentioned this pull request Oct 17, 2024
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

3 participants