forked from opensciencegrid/stashcp
-
Notifications
You must be signed in to change notification settings - Fork 6
/
errorAccum.go
108 lines (98 loc) · 2.4 KB
/
errorAccum.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package stashcp
import (
"errors"
"fmt"
"net/http"
"sync"
"time"
grab "github.com/cavaliercoder/grab"
)
type TimestampedError struct {
err error
timestamp time.Time
}
var (
bunchOfErrors []TimestampedError
mu sync.Mutex
// We will generate an error string including the time since startup
startup time.Time = time.Now()
)
// AddError will add an accumulated error to the error stack
func AddError(err error) bool {
mu.Lock()
defer mu.Unlock()
bunchOfErrors = append(bunchOfErrors, TimestampedError{err, time.Now()})
return true
}
func ClearErrors() {
mu.Lock()
defer mu.Unlock()
bunchOfErrors = make([]TimestampedError, 0)
}
func GetErrors() string {
mu.Lock()
defer mu.Unlock()
first := true
lastError := startup
var errorsFormatted []string
for idx, theError := range bunchOfErrors {
errFmt := fmt.Sprintf("Attempt #%v: %s", idx + 1, theError.err.Error())
timeElapsed := theError.timestamp.Sub(lastError)
timeFormat := timeElapsed.Truncate(100*time.Millisecond).String()
errFmt += " (" + timeFormat
if first {
errFmt += " since start)"
} else {
timeSinceStart := theError.timestamp.Sub(startup)
timeSinceStartFormat := timeSinceStart.Truncate(100*time.Millisecond).String()
errFmt += " elapsed, " + timeSinceStartFormat + " since start)"
}
lastError = theError.timestamp
errorsFormatted = append(errorsFormatted, errFmt)
first = false
}
var toReturn string
first = true
for idx := len(errorsFormatted)-1; idx >= 0; idx-- {
if !first {
toReturn += "; "
}
toReturn += errorsFormatted[idx]
first = false
}
return toReturn
}
// IsRetryable will return true if the error is retryable
func IsRetryable(err error) bool {
if errors.Is(err, &SlowTransferError{}) {
return true
}
var cse *ConnectionSetupError
if errors.As(err, &cse) {
if sce, ok := cse.Unwrap().(grab.StatusCodeError); ok {
switch int(sce) {
case http.StatusInternalServerError:
case http.StatusBadGateway:
case http.StatusServiceUnavailable:
case http.StatusGatewayTimeout:
return true
default:
return false
}
}
return true
}
return false
}
// ErrorsRetryable returns if the errors in the stack are retryable later
func ErrorsRetryable() bool {
mu.Lock()
defer mu.Unlock()
// Loop through the errors and see if all of them are retryable
for _, theError := range bunchOfErrors {
if !IsRetryable(theError.err) {
return false
}
}
return true
}