-
Notifications
You must be signed in to change notification settings - Fork 10
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
Conversation
WalkthroughThe changes introduce profiling capabilities to the application by adding new configuration flags and a profiling server. The Changes
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
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 theProfileServer
struct and its methods.Consider adding comments to:
- The
ProfileServer
struct, explaining its purpose and usage.- 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
andprofiler-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 implementedThe 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:
- New fields added to the
Config
struct for profiler settings.- Corresponding flags added to the
FromFlags
function for command-line configuration.- 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:
- Add a note about the performance impact of enabling the profiler in production environments.
- Mention that the profiler should be used cautiously in production due to potential security risks.
- Add examples of what kind of insights developers can gain from the profiler output.
- 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 improvementsThere are a few minor markdown formatting issues that could be addressed to improve the document's consistency and readability:
On line 183, remove the spaces inside the code span element: Change
`testnet `
to`testnet`
.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=6060go tool pprof -http :2000 http://localhost:6060/debug/pprof/profilecurl --output trace.out http://localhost:6060/debug/pprof/trace go tool trace -http :2001 trace.outThese 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
📒 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 serverendpoint
to store the server's addressThis 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 formattingThe 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=6060Then, 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)
func (s *ProfileServer) Stop() error { | ||
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) | ||
defer cancel() | ||
|
||
return s.server.Shutdown(ctx) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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) | ||
} | ||
}() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
--coa-key=2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 \ | ||
--wallet-api-key=2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Move the API keys to environment variables as suggested earlier.
- Ensure that the
.env
file (if used) is added to.gitignore
. - 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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
b.logger.Err(err).Msg("Profiler server graceful shutdown failed") | |
b.logger.Error().Err(err).Msg("Profiler server graceful shutdown failed") |
b.profiler.Start() | ||
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr()) | ||
|
||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome 💯
Closes: #???
Description
Add a profiler to the gateway
For contributor use:
master
branchFiles changed
in the Github PR explorerSummary by CodeRabbit
New Features
profiler-enabled
,profiler-host
, andprofiler-port
.Documentation
Bug Fixes