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

GODRIVER-2603 Use errors.Is/As for IsTimeout and IsDuplicateKeyError. #1371

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 40 additions & 34 deletions mongo/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,50 +102,56 @@ func replaceErrors(err error) error {
return err
}

// IsDuplicateKeyError returns true if err is a duplicate key error
// IsDuplicateKeyError returns true if err is a duplicate key error.
func IsDuplicateKeyError(err error) bool {
// handles SERVER-7164 and SERVER-11493
for ; err != nil; err = unwrap(err) {
if e, ok := err.(ServerError); ok {
return e.HasErrorCode(11000) || e.HasErrorCode(11001) || e.HasErrorCode(12582) ||
e.HasErrorCodeWithMessage(16460, " E11000 ")
}
if se := ServerError(nil); errors.As(err, &se) {
return se.HasErrorCode(11000) || // Duplicate key error.
se.HasErrorCode(11001) || // Duplicate key error on update.
// Duplicate key error in a capped collection. See SERVER-7164.
se.HasErrorCode(12582) ||
// Mongos insert error caused by a duplicate key error. See
// SERVER-11493.
se.HasErrorCodeWithMessage(16460, " E11000 ")
}
return false
}

// IsTimeout returns true if err is from a timeout
// timeoutErrs is a list of error values that indicate a timeout happened.
var timeoutErrs = [...]error{
context.DeadlineExceeded,
driver.ErrDeadlineWouldBeExceeded,
topology.ErrServerSelectionTimeout,
}

// IsTimeout returns true if err was caused by a timeout. For error chains,
// IsTimeout returns true if any error in the chain was caused by a timeout.
func IsTimeout(err error) bool {
for ; err != nil; err = unwrap(err) {
// check unwrappable errors together
if err == context.DeadlineExceeded {
return true
}
if err == driver.ErrDeadlineWouldBeExceeded {
return true
}
if err == topology.ErrServerSelectionTimeout {
return true
}
if _, ok := err.(topology.WaitQueueTimeoutError); ok {
return true
}
if ce, ok := err.(CommandError); ok && ce.IsMaxTimeMSExpiredError() {
// Check if the error chain contains any of the timeout error values.
for _, target := range timeoutErrs {
if errors.Is(err, target) {
return true
}
if we, ok := err.(WriteException); ok && we.WriteConcernError != nil &&
we.WriteConcernError.IsMaxTimeMSExpiredError() {
}

// Check if the error chain contains any error types that can indicate
// timeout.
if errors.As(err, &topology.WaitQueueTimeoutError{}) {
return true
}
if ce := (CommandError{}); errors.As(err, &ce) && ce.IsMaxTimeMSExpiredError() {
return true
}
if we := (WriteException{}); errors.As(err, &we) && we.WriteConcernError != nil && we.WriteConcernError.IsMaxTimeMSExpiredError() {
return true
}
if ne := net.Error(nil); errors.As(err, &ne) {
return ne.Timeout()
}
// Check timeout error labels.
if le := LabeledError(nil); errors.As(err, &le) {
if le.HasErrorLabel("NetworkTimeoutError") || le.HasErrorLabel("ExceededTimeLimitError") {
return true
}
if ne, ok := err.(net.Error); ok {
return ne.Timeout()
}
//timeout error labels
if le, ok := err.(LabeledError); ok {
if le.HasErrorLabel("NetworkTimeoutError") || le.HasErrorLabel("ExceededTimeLimitError") {
return true
}
}
}

return false
Expand Down
2 changes: 1 addition & 1 deletion mongo/integration/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ func TestClient(t *testing.T) {
err := mt.Client.Ping(ctx, nil)
cancel()
assert.NotNil(mt, err, "expected Ping to return an error")
assert.True(mt, mongo.IsTimeout(err), "expected a timeout error: got %v", err)
assert.True(mt, mongo.IsTimeout(err), "expected a timeout error, got: %v", err)
}

// Assert that the Ping timeouts result in no connections being closed.
Expand Down
9 changes: 7 additions & 2 deletions x/mongo/driver/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,15 @@ func (e Error) UnsupportedStorageEngine() bool {

// Error implements the error interface.
func (e Error) Error() string {
var msg string
if e.Name != "" {
return fmt.Sprintf("(%v) %v", e.Name, e.Message)
msg = fmt.Sprintf("(%v)", e.Name)
}
return e.Message
msg += " " + e.Message
if e.Wrapped != nil {
msg += ": " + e.Wrapped.Error()
}
return msg
}

// Unwrap returns the underlying error.
Expand Down
Loading