mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #39021 from wojtek-t/retry_conn_reset_by_peer
Automatic merge from submit-queue Automatically retry "connection reset by peer" errors Ref #38776
This commit is contained in:
commit
b6d7fa7d5f
@ -811,7 +811,20 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
|
||||
r.backoffMgr.UpdateBackoff(r.URL(), err, resp.StatusCode)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
// "Connection reset by peer" is usually a transient error.
|
||||
// Thus in case of "GET" operations, we simply retry it.
|
||||
// We are not automatically retrying "write" operations, as
|
||||
// they are not idempotent.
|
||||
if !net.IsConnectionReset(err) || r.verb != "GET" {
|
||||
return err
|
||||
}
|
||||
// For the purpose of retry, we set the artificial "retry-after" response.
|
||||
// TODO: Should we clean the original response if it exists?
|
||||
resp = &http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Header: http.Header{"Retry-After": []string{"1"}},
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
}
|
||||
}
|
||||
|
||||
done := func() bool {
|
||||
|
@ -22,12 +22,14 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -1120,6 +1122,35 @@ func TestCheckRetryClosesBody(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectionResetByPeerIsRetried(t *testing.T) {
|
||||
count := 0
|
||||
backoff := &testBackoffManager{}
|
||||
req := &Request{
|
||||
verb: "GET",
|
||||
client: clientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
count++
|
||||
if count >= 3 {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
}, nil
|
||||
}
|
||||
return nil, &net.OpError{Err: syscall.ECONNRESET}
|
||||
}),
|
||||
backoffMgr: backoff,
|
||||
}
|
||||
// We expect two retries of "connection reset by peer" and the success.
|
||||
_, err := req.Do().Raw()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
// We have a sleep before each retry (including the initial one) and for
|
||||
// every "retry-after" call - thus 5 together.
|
||||
if len(backoff.sleeps) != 5 {
|
||||
t.Errorf("Expected 5 retries, got: %d", len(backoff.sleeps))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckRetryHandles429And5xx(t *testing.T) {
|
||||
count := 0
|
||||
ch := make(chan struct{})
|
||||
|
@ -19,6 +19,7 @@ package net
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// IPNetEqual checks if the two input IPNets are representing the same subnet.
|
||||
@ -34,3 +35,12 @@ func IPNetEqual(ipnet1, ipnet2 *net.IPNet) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns if the given err is "connection reset by peer" error.
|
||||
func IsConnectionReset(err error) bool {
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if ok && opErr.Err.Error() == syscall.ECONNRESET.Error() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user