Skip to content

Commit 7855000

Browse files
authored
Merge pull request avast#89 from dillonstreator/no-preallocation
remove error log pre-allocation and add benchmark
2 parents 72ba19c + 8f58a1e commit 7855000

File tree

2 files changed

+57
-52
lines changed

2 files changed

+57
-52
lines changed

retry.go

Lines changed: 31 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -117,72 +117,61 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
117117
return nil
118118
}
119119

120-
var errorLog Error
121-
if !config.lastErrorOnly {
122-
errorLog = make(Error, config.attempts)
123-
} else {
124-
errorLog = make(Error, 1)
125-
}
120+
errorLog := Error{}
126121

127122
attemptsForError := make(map[error]uint, len(config.attemptsForError))
128123
for err, attempts := range config.attemptsForError {
129124
attemptsForError[err] = attempts
130125
}
131126

132-
lastErrIndex := n
133127
shouldRetry := true
134128
for shouldRetry {
135129
err := retryableFunc()
130+
if err == nil {
131+
return nil
132+
}
136133

137-
if err != nil {
138-
errorLog[lastErrIndex] = unpackUnrecoverable(err)
134+
errorLog = append(errorLog, unpackUnrecoverable(err))
139135

140-
if !config.retryIf(err) {
141-
break
142-
}
136+
if !config.retryIf(err) {
137+
break
138+
}
143139

144-
config.onRetry(n, err)
140+
config.onRetry(n, err)
145141

146-
for errToCheck, attempts := range attemptsForError {
147-
if errors.Is(err, errToCheck) {
148-
attempts--
149-
attemptsForError[errToCheck] = attempts
150-
shouldRetry = shouldRetry && attempts > 0
151-
}
142+
for errToCheck, attempts := range attemptsForError {
143+
if errors.Is(err, errToCheck) {
144+
attempts--
145+
attemptsForError[errToCheck] = attempts
146+
shouldRetry = shouldRetry && attempts > 0
152147
}
148+
}
153149

154-
// if this is last attempt - don't wait
155-
if n == config.attempts-1 {
156-
break
157-
}
150+
// if this is last attempt - don't wait
151+
if n == config.attempts-1 {
152+
break
153+
}
158154

159-
select {
160-
case <-config.timer.After(delay(config, n, err)):
161-
case <-config.context.Done():
162-
if config.lastErrorOnly {
163-
return config.context.Err()
164-
}
165-
n++
166-
errorLog[n] = config.context.Err()
167-
return errorLog[:lenWithoutNil(errorLog)]
155+
select {
156+
case <-config.timer.After(delay(config, n, err)):
157+
case <-config.context.Done():
158+
if config.lastErrorOnly {
159+
return config.context.Err()
168160
}
161+
n++
169162

170-
} else {
171-
return nil
163+
return append(errorLog, config.context.Err())
172164
}
173165

174166
n++
175167
shouldRetry = shouldRetry && n < config.attempts
176-
177-
if !config.lastErrorOnly {
178-
lastErrIndex = n
179-
}
180168
}
181169

182170
if config.lastErrorOnly {
183-
return errorLog[lastErrIndex]
171+
return errorLog.Unwrap()
184172
}
185-
return errorLog[:lenWithoutNil(errorLog)]
173+
174+
return errorLog
186175
}
187176

188177
func newDefaultRetryConfig() *Config {
@@ -206,7 +195,7 @@ type Error []error
206195
// Error method return string representation of Error
207196
// It is an implementation of error interface
208197
func (e Error) Error() string {
209-
logWithNumber := make([]string, lenWithoutNil(e))
198+
logWithNumber := make([]string, len(e))
210199
for i, l := range e {
211200
if l != nil {
212201
logWithNumber[i] = fmt.Sprintf("#%d: %s", i+1, l.Error())
@@ -250,17 +239,7 @@ When you need to unwrap all errors, you should use `WrappedErrors()` instead.
250239
Added in version 4.2.0.
251240
*/
252241
func (e Error) Unwrap() error {
253-
return e[lenWithoutNil(e)-1]
254-
}
255-
256-
func lenWithoutNil(e Error) (count int) {
257-
for _, v := range e {
258-
if v != nil {
259-
count++
260-
}
261-
}
262-
263-
return
242+
return e[len(e)-1]
264243
}
265244

266245
// WrappedErrors returns the list of errors that this Error is wrapping.

retry_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,32 @@ func TestUnwrap(t *testing.T) {
526526
assert.Equal(t, testError, errors.Unwrap(err))
527527
}
528528

529+
func BenchmarkDo(b *testing.B) {
530+
testError := errors.New("test error")
531+
532+
for i := 0; i < b.N; i++ {
533+
Do(
534+
func() error {
535+
return testError
536+
},
537+
Attempts(10),
538+
Delay(0),
539+
)
540+
}
541+
}
542+
543+
func BenchmarkDoNoErrors(b *testing.B) {
544+
for i := 0; i < b.N; i++ {
545+
Do(
546+
func() error {
547+
return nil
548+
},
549+
Attempts(10),
550+
Delay(0),
551+
)
552+
}
553+
}
554+
529555
func TestIsRecoverable(t *testing.T) {
530556
err := errors.New("err")
531557
assert.True(t, IsRecoverable(err))

0 commit comments

Comments
 (0)