Retry unexpected EOF errors (#4037)
<!-- PR description--> Adds `io.ErrUnexpectedEOF` to the list of retriable errors looked up by retry handler. --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [ ] 🌻 Feature - [x] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * #<issue> #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
56820e95e0
commit
77ab14b5df
@ -28,6 +28,7 @@ const (
|
||||
// between callers.
|
||||
var retryErrs = []error{
|
||||
syscall.ECONNRESET,
|
||||
io.ErrUnexpectedEOF,
|
||||
}
|
||||
|
||||
type Getter interface {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package readers_test
|
||||
package readers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/readers"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
)
|
||||
|
||||
@ -141,7 +140,7 @@ func (suite *ResetRetryHandlerUnitSuite) TestResetRetryHandler() {
|
||||
expectErr error
|
||||
}{
|
||||
{
|
||||
name: "OnlyFirstGetErrors NoRangeSupport",
|
||||
name: "OnlyFirstGetErrors ECONNRESET NoRangeSupport",
|
||||
getterResps: map[int]getterResp{
|
||||
0: {
|
||||
err: syscall.ECONNRESET,
|
||||
@ -149,6 +148,15 @@ func (suite *ResetRetryHandlerUnitSuite) TestResetRetryHandler() {
|
||||
},
|
||||
expectData: data,
|
||||
},
|
||||
{
|
||||
name: "OnlyFirstGetErrors ErrUnexpectedEOF NoRangeSupport",
|
||||
getterResps: map[int]getterResp{
|
||||
0: {
|
||||
err: io.ErrUnexpectedEOF,
|
||||
},
|
||||
},
|
||||
expectData: data,
|
||||
},
|
||||
{
|
||||
name: "OnlyFirstReadErrors RangeSupport",
|
||||
supportsRange: true,
|
||||
@ -223,6 +231,29 @@ func (suite *ResetRetryHandlerUnitSuite) TestResetRetryHandler() {
|
||||
},
|
||||
expectData: data,
|
||||
},
|
||||
{
|
||||
name: "MultipleRetriableErrorTypesInMiddle RangeSupport",
|
||||
supportsRange: true,
|
||||
getterResps: map[int]getterResp{
|
||||
1: {offset: 12},
|
||||
2: {offset: 20},
|
||||
},
|
||||
getterExpectHeaders: map[int]map[string]string{
|
||||
1: {"Range": "bytes=12-"},
|
||||
2: {"Range": "bytes=20-"},
|
||||
},
|
||||
readerResps: map[int]readResp{
|
||||
3: {
|
||||
read: 0,
|
||||
err: io.ErrUnexpectedEOF,
|
||||
},
|
||||
6: {
|
||||
read: 0,
|
||||
err: syscall.ECONNRESET,
|
||||
},
|
||||
},
|
||||
expectData: data,
|
||||
},
|
||||
{
|
||||
name: "ShortReadWithError NoRangeSupport",
|
||||
readerResps: map[int]readResp{
|
||||
@ -450,7 +481,7 @@ func (suite *ResetRetryHandlerUnitSuite) TestResetRetryHandler() {
|
||||
resData = make([]byte, len(data))
|
||||
)
|
||||
|
||||
rrh, err := readers.NewResetRetryHandler(ctx, getter)
|
||||
rrh, err := NewResetRetryHandler(ctx, getter)
|
||||
require.NoError(t, err, "making reader wrapper: %v", clues.ToCore(err))
|
||||
|
||||
for err == nil && offset < len(data) {
|
||||
@ -475,3 +506,65 @@ func (suite *ResetRetryHandlerUnitSuite) TestResetRetryHandler() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ResetRetryHandlerUnitSuite) TestIsRetriable() {
|
||||
table := []struct {
|
||||
name string
|
||||
err error
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
err: nil,
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "Connection Reset Error",
|
||||
err: syscall.ECONNRESET,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "Unexpected EOF Error",
|
||||
err: io.ErrUnexpectedEOF,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "Not Retriable Error",
|
||||
err: assert.AnError,
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "Chained Errors With No Retriables",
|
||||
err: clues.Stack(assert.AnError, clues.New("another error")),
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "Chained Errors With ECONNRESET",
|
||||
err: clues.Stack(assert.AnError, syscall.ECONNRESET, assert.AnError),
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "Chained Errors With ErrUnexpectedEOF",
|
||||
err: clues.Stack(assert.AnError, io.ErrUnexpectedEOF, assert.AnError),
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "Wrapped ECONNRESET Error",
|
||||
err: clues.Wrap(syscall.ECONNRESET, "wrapped error"),
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "Wrapped ErrUnexpectedEOF Error",
|
||||
err: clues.Wrap(io.ErrUnexpectedEOF, "wrapped error"),
|
||||
expect: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
assert.Equal(t, test.expect, isRetriable(test.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user