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 more generic error predicates #5675

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
3 changes: 3 additions & 0 deletions .changelog/3116.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
Added retries for common network errors we've encountered.
```
62 changes: 56 additions & 6 deletions google/error_retry_predicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package google

import (
"fmt"
"io"
"log"
"net"
"net/url"
"strings"

Expand All @@ -14,21 +16,69 @@ type RetryErrorPredicateFunc func(error) (bool, string)
/** ADD GLOBAL ERROR RETRY PREDICATES HERE **/
// Retry predicates that shoud apply to all requests should be added here.
var defaultErrorRetryPredicates = []RetryErrorPredicateFunc{
isUrlTimeoutError,
// Common network errors (usually wrapped by URL error)
isNetworkTemporaryError,
isNetworkTimeoutError,
isIoEOFError,
isConnectionResetNetworkError,

// Common GCP error codes
isCommonRetryableErrorCode,

//While this might apply only to Cloud SQL, historically,
// we had this in our global default error retries, so it is a default
// for now.
// we had this in our global default error retries.
// Keeping it as a default for now.
is409OperationInProgressError,
}

/** END GLOBAL ERROR RETRY PREDICATES HERE **/

func isUrlTimeoutError(err error) (bool, string) {
func isNetworkTemporaryError(err error) (bool, string) {
if netErr, ok := err.(*net.OpError); ok && netErr.Temporary() {
return true, "marked as timeout"
}
if urlerr, ok := err.(*url.Error); ok && urlerr.Temporary() {
return true, "marked as timeout"
}
return false, ""
}

func isNetworkTimeoutError(err error) (bool, string) {
if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() {
return true, "marked as timeout"
}
if urlerr, ok := err.(*url.Error); ok && urlerr.Timeout() {
log.Printf("[DEBUG] Dismissed an error as retryable based on googleapis.com target: %s", err)
return true, "Got URL timeout error"
return true, "marked as timeout"
}
return false, ""
}

func isIoEOFError(err error) (bool, string) {
if err == io.ErrUnexpectedEOF {
return true, "Got unexpected EOF"
}

if urlerr, urlok := err.(*url.Error); urlok {
wrappedErr := urlerr.Unwrap()
if wrappedErr == io.ErrUnexpectedEOF {
return true, "Got unexpected EOF"
}
}
return false, ""
}

const connectionResetByPeerErr = "connection reset by peer"

func isConnectionResetNetworkError(err error) (bool, string) {
neterr, ok := err.(*net.OpError)
if !ok {
if urlerr, urlok := err.(*url.Error); urlok {
wrappedErr := urlerr.Unwrap()
neterr, ok = wrappedErr.(*net.OpError)
}
}
if ok && neterr.Err.Error() == connectionResetByPeerErr {
return true, fmt.Sprintf("Connection reset by peer")
}
return false, ""
}
Expand Down