forked from coinbase/mesh-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
146 lines (126 loc) · 4.01 KB
/
utils.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2020 Coinbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fetcher
import (
"errors"
"fmt"
"io"
"log"
"strings"
"time"
"github.com/cenkalti/backoff"
"github.com/coinbase/rosetta-sdk-go/client"
"github.com/coinbase/rosetta-sdk-go/types"
)
const (
// connectionResetByPeer is returned when the server resets a connection
// because of high memory usage or because a client has opened too many
// connections.
connectionResetByPeer = "connection reset by peer"
// clientTimeout is returned when a request exceeds the set
// HTTP timeout setting.
clientTimeout = "Client.Timeout exceeded"
// serverClosedIdleConnection is returned when the client
// attempts to make a request on a connection that was closed
// by the server.
serverClosedIdleConnection = "server closed idle connection"
)
// Backoff wraps backoff.BackOff so we can
// access the retry count (which is private
// on backoff.BackOff).
type Backoff struct {
backoff backoff.BackOff
attempts int
}
// backoffRetries creates the backoff.BackOff struct used by all
// *Retry functions in the fetcher.
func backoffRetries(
maxElapsedTime time.Duration,
maxRetries uint64,
) *Backoff {
exponentialBackoff := backoff.NewExponentialBackOff()
exponentialBackoff.MaxElapsedTime = maxElapsedTime
return &Backoff{backoff: backoff.WithMaxRetries(exponentialBackoff, maxRetries)}
}
// transientError returns a boolean indicating if a particular
// error is considered transient (so the request should be
// retried). Currently, we consider EOF, connection reset by
// peer, and timeout to be transient.
func transientError(err error) bool {
if errors.Is(err, client.ErrRetriable) ||
strings.Contains(err.Error(), io.EOF.Error()) ||
strings.Contains(err.Error(), connectionResetByPeer) ||
strings.Contains(err.Error(), serverClosedIdleConnection) ||
strings.Contains(err.Error(), clientTimeout) {
return true
}
return false
}
// tryAgain handles a backoff and prints error messages depending
// on the fetchMsg.
func tryAgain(fetchMsg string, thisBackoff *Backoff, err *Error) *Error {
if !err.Retry {
return err
}
nextBackoff := thisBackoff.backoff.NextBackOff()
if nextBackoff == backoff.Stop {
return &Error{
Err: fmt.Errorf(
"%w: %s",
ErrExhaustedRetries,
fetchMsg,
),
}
}
errMessage := err.Err.Error()
if err.ClientErr != nil {
errMessage = types.PrintStruct(err.ClientErr)
}
thisBackoff.attempts++
log.Printf(
"%s: retrying fetch for %s after %fs (prior attempts: %d)\n",
errMessage,
fetchMsg,
nextBackoff.Seconds(),
thisBackoff.attempts,
)
time.Sleep(nextBackoff)
return nil
}
// checkError compares a *fetcher.Error to a simple type error and returns
// a boolean indicating if they are equivalent
func checkError(fetcherErr *Error, err error) bool {
if fetcherErr == nil {
return err == nil
}
return errors.Is(fetcherErr.Err, err)
}
// CheckNetworkListForNetwork returns a boolean
// indicating if a *types.NetworkIdentifier is present
// in the list of supported networks.
func CheckNetworkListForNetwork(
networkList *types.NetworkListResponse,
networkIdentifier *types.NetworkIdentifier,
) (bool, []*types.NetworkIdentifier) {
networkMatched := false
supportedNetworks := []*types.NetworkIdentifier{}
for _, availableNetwork := range networkList.NetworkIdentifiers {
if types.Hash(availableNetwork) == types.Hash(networkIdentifier) {
networkMatched = true
break
}
supportedNetworks = append(supportedNetworks, availableNetwork)
}
return networkMatched, supportedNetworks
}