8
8
"fmt"
9
9
"io"
10
10
"log"
11
- "net "
11
+ "math "
12
12
"net/http"
13
13
"os"
14
14
"sync"
@@ -25,6 +25,52 @@ type Config struct {
25
25
GithubApiToken string `json:"GithubApiToken"`
26
26
}
27
27
28
+ type retryableTransport struct {
29
+ transport http.RoundTripper
30
+ TLSHandshakeTimeout time.Duration
31
+ ResponseHeaderTimeout time.Duration
32
+ }
33
+
34
+ const retryCount = 3
35
+
36
+ func shouldRetry (err error , resp * http.Response ) bool {
37
+ if err != nil {
38
+ return true
39
+ }
40
+ switch resp .StatusCode {
41
+ case http .StatusInternalServerError , http .StatusBadGateway , http .StatusServiceUnavailable , http .StatusGatewayTimeout :
42
+ return true
43
+ default :
44
+ return false
45
+ }
46
+ }
47
+
48
+ func (t * retryableTransport ) RoundTrip (req * http.Request ) (* http.Response , error ) {
49
+ var bodyBytes []byte
50
+ if req .Body != nil {
51
+ bodyBytes , _ = io .ReadAll (req .Body )
52
+ req .Body = io .NopCloser (bytes .NewBuffer (bodyBytes ))
53
+ }
54
+ resp , err := t .transport .RoundTrip (req )
55
+ retries := 0
56
+ for shouldRetry (err , resp ) && retries < retryCount {
57
+ backoff := time .Duration (math .Pow (2 , float64 (retries ))) * time .Second
58
+ time .Sleep (backoff )
59
+ if resp .Body != nil {
60
+ io .Copy (io .Discard , resp .Body )
61
+ resp .Body .Close ()
62
+ }
63
+ if req .Body != nil {
64
+ req .Body = io .NopCloser (bytes .NewBuffer (bodyBytes ))
65
+ }
66
+ log .Printf ("Previous request failed with %s" , resp .Status )
67
+ log .Printf ("Retry %d of request to: %s" , retries + 1 , req .URL )
68
+ resp , err = t .transport .RoundTrip (req )
69
+ retries ++
70
+ }
71
+ return resp , err
72
+ }
73
+
28
74
func main () {
29
75
confFilePath := "github_exporter.json"
30
76
confData , err := os .Open (confFilePath )
@@ -53,15 +99,14 @@ func main() {
53
99
log .Fatalln ("GithubApiToken is required" )
54
100
}
55
101
102
+ transport := & retryableTransport {
103
+ transport : & http.Transport {},
104
+ TLSHandshakeTimeout : 30 * time .Second ,
105
+ ResponseHeaderTimeout : 30 * time .Second ,
106
+ }
56
107
client := & http.Client {
57
- Timeout : 30 * time .Second ,
58
- Transport : & http.Transport {
59
- DialContext : (& net.Dialer {
60
- Timeout : 30 * time .Second ,
61
- }).DialContext ,
62
- TLSHandshakeTimeout : 30 * time .Second ,
63
- ResponseHeaderTimeout : 30 * time .Second ,
64
- },
108
+ Timeout : 30 * time .Second ,
109
+ Transport : transport ,
65
110
}
66
111
ghc := github .NewClient (client ).WithAuthToken (config .GithubApiToken )
67
112
opts := & github.RepositoryListByAuthenticatedUserOptions {Type : "owner" }
0 commit comments