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

remove errBadConnNoWrite and markBadConn #1583

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

methane
Copy link
Member

@methane methane commented May 16, 2024

Description

Fix #1582.

Checklist

  • Code compiles correctly
  • Created tests which fail without the change (if possible)
  • All tests passing
  • Extended the README / documentation, if necessary
  • Added myself / the copyright holder to the AUTHORS file

Summary by CodeRabbit

  • New Features

    • Added IsValid method to enhance connection validation.
    • Implemented SessionResetter and Validator interfaces for improved session management.
  • Bug Fixes

    • Improved error handling across various methods to ensure more reliable connections and operations.
  • Refactor

    • Streamlined error handling and control flow in packet writing methods for better performance and maintainability.
  • Chores

    • Removed unnecessary logging and deprecated error declarations to clean up the codebase.

Copy link

coderabbitai bot commented May 16, 2024

Walkthrough

The changes primarily involve refactoring error handling and logging within the mysqlConn struct. The markBadConn method and related error handling have been removed, and error returns have been streamlined. Additionally, the writePacket method and related packet handling methods have been refactored to improve control flow and error management.

Changes

File Change Summary
connection.go Removed markBadConn method, log calls, replaced ErrInvalidConn with error returns, added IsValid and SessionResetter implementations.
errors.go Removed errBadConnNoWrite error declaration and related comments.
packets.go Refactored writePacket method and various packet handling methods for improved error handling.

Assessment against linked issues

Objective Addressed Explanation
Remove markBadConn, errBadConnNoWrite, and related logging (#1582)
Implement IsValid and SessionResetter for connection validation (#1582)
Improve error handling in packet methods without translating to ErrBadConn (#1582)

Poem

In code's vast and winding lane,
We refactored with no disdain.
Errors now flow, clear and bright,
Connections strong, in the night.
No bad marks, just pure delight,
Our MySQL sings, in the moonlight. 🌙


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

@methane methane requested a review from a team May 16, 2024 08:51
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: 0

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between af8d793 and 0c65ab7.
Files selected for processing (5)
  • connection.go (11 hunks)
  • connection_test.go (2 hunks)
  • errors.go (1 hunks)
  • packets.go (8 hunks)
  • statement.go (2 hunks)
Files skipped from review due to trivial changes (1)
  • errors.go
Additional comments not posted (20)
connection_test.go (2)

171-172: Good use of errors.Is for error comparison.


188-189: Good use of errors.Is for error comparison.

statement.go (2)

60-60: Simplified error handling by directly returning the error.


103-103: Simplified error handling by directly returning the error.

connection.go (6)

120-120: Simplified error handling by directly returning the error.


174-174: Simplified error handling by directly returning the error.


207-208: Simplified error handling by directly returning the error.


318-318: Simplified error handling by directly returning the error.


462-462: Simplified error handling by directly returning the error.


668-669: Good addition of the IsValid method to implement the driver.Validator interface.

packets.go (10)

125-125: Ensure proper cleanup on write error.

The call to mc.cleanup() on write error ensures that resources are properly released. This is a good practice to avoid resource leaks.


130-131: Return specific error on write failure.

Returning the specific error err on write failure helps in better error diagnosis and handling.


132-134: Handle short write scenario.

Returning io.ErrShortWrite when n is not equal to size + 4 ensures that partial writes are correctly identified and handled.


299-300: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


388-389: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


408-409: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


427-428: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


448-449: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


990-991: Ensure proper cleanup on buffer error.

The call to mc.cleanup() on buffer error ensures that resources are properly released. This is a good practice to avoid resource leaks.


1189-1190: Ensure proper cleanup on buffer store error.

The call to mc.cleanup() on buffer store error ensures that resources are properly released. This is a good practice to avoid resource leaks.

@coveralls
Copy link

coveralls commented May 16, 2024

Coverage Status

coverage: 82.386% (-0.02%) from 82.407%
when pulling 30b95e8 on methane:bye-errBadConnNoWrite
into 9b8d28e on go-sql-driver:master.

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: 0

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 0c65ab7 and f554b55.
Files selected for processing (2)
  • connection.go (11 hunks)
  • packets.go (9 hunks)
Files skipped from review as they are similar to previous changes (2)
  • connection.go
  • packets.go

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: 0

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between f554b55 and db0cc0e.
Files selected for processing (5)
  • connection.go (11 hunks)
  • connection_test.go (2 hunks)
  • errors.go (1 hunks)
  • packets.go (9 hunks)
  • statement.go (2 hunks)
Files skipped from review as they are similar to previous changes (5)
  • connection.go
  • connection_test.go
  • errors.go
  • packets.go
  • statement.go

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

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between db0cc0e and f58f76c.
Files selected for processing (2)
  • connection.go (11 hunks)
  • packets.go (9 hunks)
Files skipped from review as they are similar to previous changes (1)
  • packets.go
Additional comments not posted (5)
connection.go (5)

120-120: Ensure proper error handling in the begin method.


323-323: Ensure that the error handling in Exec is consistent with the new error handling strategy.


331-331: Review the error handling in exec to ensure it aligns with the new error handling strategy.


471-471: Confirm that the error handling in Ping aligns with the new error handling strategy.


677-678: Confirm implementation of interfaces driver.SessionResetter and driver.Validator.

The implementation of these interfaces is crucial for the new connection validation logic. Good to see this aligned with the PR objectives.

Also applies to: 685-685

connection.go Outdated
Comment on lines 209 to 213
mc.cleanup()
// interpolateParams would be called before sending any query.
// So its safe to retry.
mc.log(err)
return "", ErrInvalidConn
return "", driver.ErrBadConn
Copy link

@coderabbitai coderabbitai bot May 18, 2024

Choose a reason for hiding this comment

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

Consider simplifying the error handling in interpolateParams.

- mc.cleanup()
- mc.log(err)
+ return "", err

This change would make the error handling more straightforward and consistent with other parts of the code.


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.

Suggested change
mc.cleanup()
// interpolateParams would be called before sending any query.
// So its safe to retry.
mc.log(err)
return "", ErrInvalidConn
return "", driver.ErrBadConn
// interpolateParams would be called before sending any query.
// So its safe to retry.
return "", err

Copy link
Member Author

Choose a reason for hiding this comment

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

This is simply a bug fix. Since it did not close the connection and also did not return ErrBadConn, if the connection was in a state where takeCompleteBuffer() failed for any reason, ErrInvalidConn could be returned repeatedly, depending on how the application was used.
This fix may need to be backported. I will create a separate PR.

Copy link

Choose a reason for hiding this comment

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

@methane, thank you for your insights and for highlighting the importance of this bug fix. Ensuring that ErrBadConn is returned correctly is crucial for the robustness of error handling in connection management. If there's anything more you need regarding the backporting process or any other assistance, please feel free to let me know. Looking forward to seeing the improvements in the upcoming PR!

@methane methane requested review from shogo82148 and removed request for shogo82148 June 5, 2024 06:51
Copy link
Member Author

@methane methane left a comment

Choose a reason for hiding this comment

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

I will split this PR to smaller PRs.

return err
}
return driver.ErrBadConn
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This function and errBadConnNoWrite were completely meaningless. There is no need to use errBadConnNoWrite once and convert it to ErrBadConn, as you can just use ErrBadConn directly if you are not sending anything yet.

func (mc *mysqlConn) Begin() (driver.Tx, error) {
return mc.begin(false)
}

func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
if mc.closed.Load() {
mc.log(ErrInvalidConn)
Copy link
Member Author

Choose a reason for hiding this comment

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

This log only confuses users and does not help. When a connection is closed, the reason should be logged if necessary.

connection.go Outdated
Comment on lines 209 to 213
mc.cleanup()
// interpolateParams would be called before sending any query.
// So its safe to retry.
mc.log(err)
return "", ErrInvalidConn
return "", driver.ErrBadConn
Copy link
Member Author

Choose a reason for hiding this comment

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

This is simply a bug fix. Since it did not close the connection and also did not return ErrBadConn, if the connection was in a state where takeCompleteBuffer() failed for any reason, ErrInvalidConn could be returned repeatedly, depending on how the application was used.
This fix may need to be backported. I will create a separate PR.

@methane methane marked this pull request as draft June 11, 2024 14:11
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: 7

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f58f76c and 30b95e8.

Files selected for processing (3)
  • connection.go (10 hunks)
  • errors.go (1 hunks)
  • packets.go (9 hunks)
Additional context used
golangci-lint
Additional comments not posted (3)
errors.go (1)

Line range hint 1-1: Ensure that the removal of errBadConnNoWrite is reflected in the entire codebase.

Verification successful

The search for errBadConnNoWrite did not produce any results, indicating that the error has been successfully removed from the codebase.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `errBadConnNoWrite` is completely removed from the codebase.

# Test: Search for the removed error. Expect: No occurrences.
rg --type go $'errBadConnNoWrite'

Length of output: 32



Script:

#!/bin/bash
# Description: Verify that there are no references to `errBadConnNoWrite` in the codebase.

# Test: Search for any references to the removed error. Expect: No occurrences.
rg 'errBadConnNoWrite'

Length of output: 22

connection.go (2)

688-696: Validate the implementation of interfaces.

The implementation of the driver.SessionResetter and driver.Validator interfaces in the mysqlConn struct is correct and follows the expected patterns.


342-342: Check for potential data races or concurrency issues.

Verification successful

The analysis did not reveal any potential data races or concurrency issues in the connection.go file. The functions and methods do not use concurrency primitives or shared state that would typically lead to data races.

  • No go statements, sync package usage, or other concurrency-related patterns were found in the critical functions.
  • No global variables or constants that could indicate shared state across multiple functions were found.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Check for potential data races in the `exec` method.

# Test: Run Go's race detector on the `connection.go` file.
go run -race ./connection.go

Length of output: 100



Script:

#!/bin/bash
# Locate Go files in the repository
fd -e go

# Check for the `exec` method in the `connection.go` file
ast-grep --lang go --pattern 'func exec($_, $_) $_' connection.go

Length of output: 513


Script:

#!/bin/bash
# Description: Check for potential data races in the `exec` method.

# Ensure Go environment is set up correctly and rerun the race detector
if command -v go &> /dev/null
then
    go run -race ./connection.go
else
    echo "Go environment is not set up correctly."
fi

Length of output: 104



Script:

#!/bin/bash
# Search for the `exec` method in the `connection.go` file
rg 'func exec' connection.go

Length of output: 30



Script:

#!/bin/bash
# List all methods in the `connection.go` file
ast-grep --lang go --pattern 'func $_($_) $_' connection.go

Length of output: 61



Script:

#!/bin/bash
# Search for the `func` keyword in the `connection.go` file to list all functions
rg 'func ' connection.go

Length of output: 1911



Script:

#!/bin/bash
# Search for potential data races in critical functions by looking for concurrency primitives and patterns

# Functions to analyze
functions=("Exec" "exec" "Query" "query" "Begin" "BeginTx" "Close" "Ping" "Prepare" "PrepareContext" "ExecContext" "QueryContext")

# Search for `go` statements, `sync` package usage, and shared variable access in the specified functions
for func in "${functions[@]}"; do
    echo "Analyzing function: $func"
    rg "func $func" -A 20 connection.go | rg -e 'go ' -e 'sync\.' -e '\*sync\.' -e '\.Lock' -e '\.Unlock' -e '\.RLock' -e '\.RUnlock' -e '\.Wait' -e '\.Signal' -e '\.Broadcast'
done

Length of output: 3417



Script:

#!/bin/bash
# Search for global variables and shared state in the `connection.go` file

# Search for variable declarations that might indicate shared state
rg 'var ' connection.go
rg 'const ' connection.go

Length of output: 236

@@ -344,15 +331,15 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
copied := mc.result
return &copied, err
}
return nil, mc.markBadConn(err)
return nil, err
Copy link

Choose a reason for hiding this comment

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

Review the error handling in the Exec method.

The error handling in the Exec method should be consistent and clear. Consider adding more specific error messages or handling for different error scenarios.

@@ -140,7 +129,7 @@
if err == nil {
return &mysqlTx{mc}, err
}
return nil, mc.markBadConn(err)
return nil, err
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling and propagation.

The error handling in the begin method should be consistent and clear. Consider adding more specific error messages or handling for different error scenarios.

Comment on lines +127 to +133
if err != nil {
mc.cleanup()
mc.log(ErrMalformPkt)
} else {
if cerr := mc.canceled.Value(); cerr != nil {
return cerr
}
if n == 0 && pktLen == len(data)-4 {
// only for the first loop iteration when nothing was written yet
return errBadConnNoWrite
}
return err
}
Copy link

Choose a reason for hiding this comment

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

Consider handling specific I/O errors more gracefully.

The current implementation returns the raw error directly to the caller. It might be beneficial to classify certain types of I/O errors and handle them differently, such as temporary network errors which could be retried. This would make the error handling more robust and potentially reduce connection failures due to transient issues.

Comment on lines +135 to +138
// io.Writer(b) must return a non-nil error if it cannot write len(b) bytes.
// The io.ErrShortWrite error is used to indicate that this rule has not been followed.
mc.cleanup()
mc.log(err)
return io.ErrShortWrite
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling when io.ErrShortWrite occurs.

The current implementation returns io.ErrShortWrite directly. It might be useful to add a log statement before returning the error for better debugging and operational visibility.

Comment on lines +302 to +303
mc.cleanup()
return err
Copy link

Choose a reason for hiding this comment

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

Refactor repeated error handling patterns.

There is a repeated pattern where a buffer acquisition fails and mc.cleanup() is called before returning the error. This pattern could be abstracted into a helper method to reduce code duplication and improve maintainability.

- mc.cleanup()
- return err
+ return handleBufferAcquisitionError(mc, err)

And then define the helper function:

func handleBufferAcquisitionError(mc *mysqlConn, err error) error {
    mc.cleanup()
    return err
}

Also applies to: 391-392, 411-412, 430-431, 451-452

Comment on lines +521 to +547
// https:https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response.html
func (mc *okHandler) readResultSetHeaderPacket() (int, error) {
// handleOkPacket replaces both values; other cases leave the values unchanged.
mc.result.affectedRows = append(mc.result.affectedRows, 0)
mc.result.insertIds = append(mc.result.insertIds, 0)

data, err := mc.conn().readPacket()
if err == nil {
switch data[0] {

case iOK:
return 0, mc.handleOkPacket(data)
if err != nil {
return 0, err
}

case iERR:
return 0, mc.conn().handleErrorPacket(data)
switch data[0] {
case iOK:
return 0, mc.handleOkPacket(data)

case iLocalInFile:
return 0, mc.handleInFileRequest(string(data[1:]))
}
case iERR:
return 0, mc.conn().handleErrorPacket(data)

// column count
num, _, _ := readLengthEncodedInteger(data)
// ignore remaining data in the packet. see #1478.
return int(num), nil
case iLocalInFile:
return 0, mc.handleInFileRequest(string(data[1:]))
}
return 0, err

// column count
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_text_resultset.html
num, _, _ := readLengthEncodedInteger(data)
// ignore remaining data in the packet. see #1478.
return int(num), nil
Copy link

Choose a reason for hiding this comment

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

Optimize the handling of result set headers.

The method readResultSetHeaderPacket could be optimized by directly returning the error from handleOkPacket and handleErrorPacket without the intermediate assignment to data and err. This would simplify the control flow and potentially improve performance by reducing unnecessary variable assignments.

- data, err := mc.conn().readPacket()
- if err != nil {
-     return 0, err
- }
- switch data[0] {
- case iOK:
-     return 0, mc.handleOkPacket(data)
- case iERR:
-     return 0, mc.conn().handleErrorPacket(data)
- case iLocalInFile:
-     return 0, mc.handleInFileRequest(string(data[1:]))
- }
+ data, err := mc.conn().readPacket()
+ if err != nil {
+     return 0, err
+ }
+ return 0, mc.handlePacketBasedOnType(data)

And then define the helper function:

func (mc *okHandler) handlePacketBasedOnType(data []byte) error {
    switch data[0] {
    case iOK:
        return mc.handleOkPacket(data)
    case iERR:
        return mc.conn().handleErrorPacket(data)
    case iLocalInFile:
        return mc.handleInFileRequest(string(data[1:]))
    default:
        return fmt.Errorf("unknown packet type: %v", data[0])
    }
}

Comment on lines +994 to +995
mc.cleanup()
return err
Copy link

Choose a reason for hiding this comment

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

Handle buffer acquisition failures more gracefully.

When buffer acquisition fails, the method writeExecutePacket simply cleans up and returns the error. Consider implementing a retry mechanism or a more informative error message to help diagnose issues related to buffer allocation failures.

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.

Improve ErrBadConn
2 participants