mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +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)
|
r.backoffMgr.UpdateBackoff(r.URL(), err, resp.StatusCode)
|
||||||
}
|
}
|
||||||
if err != nil {
|
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 {
|
done := func() bool {
|
||||||
|
@ -22,12 +22,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func TestCheckRetryHandles429And5xx(t *testing.T) {
|
||||||
count := 0
|
count := 0
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
|
@ -19,6 +19,7 @@ package net
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPNetEqual checks if the two input IPNets are representing the same subnet.
|
// 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
|
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