Skip to content
This repository was archived by the owner on Oct 28, 2022. It is now read-only.

Commit 32e4746

Browse files
committed
add retry handleing to hook calls
1 parent 0ad689d commit 32e4746

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

controller/on_match.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ var routeTestOnMatchHook = routeBuilder.R{
197197
return err
198198
}
199199

200-
headers, err := ctx.OnMatchHook.Call(bytes.NewReader(dummyDataAsJSON), models.DataKindMatch)
200+
headers, err := ctx.OnMatchHook.CallWithRetry(bytes.NewReader(dummyDataAsJSON), models.DataKindMatch)
201201
if err != nil {
202202
return err
203203
}
@@ -236,7 +236,7 @@ var routeTestOnListHook = routeBuilder.R{
236236
return err
237237
}
238238

239-
headers, err := ctx.OnMatchHook.Call(bytes.NewReader(dummyDataAsJSON), models.DataKindList)
239+
headers, err := ctx.OnMatchHook.CallWithRetry(bytes.NewReader(dummyDataAsJSON), models.DataKindList)
240240
if err != nil {
241241
return err
242242
}

models/onMatchHook.go

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package models
22

33
import (
4+
"bytes"
45
"errors"
56
"fmt"
67
"io"
78
"io/ioutil"
89
"net/http"
10+
"time"
911

1012
"github.com/apex/log"
1113
"github.com/script-development/RT-CV/db"
@@ -87,7 +89,7 @@ func (k DataKind) contentTypeAndDataKind() (contentType string, dataKind string)
8789

8890
// CallAndLogResult calls the hook defined in OnMatchHook and logs the result
8991
func (h *OnMatchHook) CallAndLogResult(body io.Reader, dataKind DataKind, logger *log.Entry) {
90-
_, err := h.Call(body, dataKind)
92+
_, err := h.CallWithRetry(body, dataKind)
9193

9294
loggerWithFields := logger.WithField("hook", h.URL).WithField("hook_id", h.ID.Hex())
9395
if err != nil {
@@ -97,15 +99,74 @@ func (h *OnMatchHook) CallAndLogResult(body io.Reader, dataKind DataKind, logger
9799
}
98100
}
99101

102+
// CallWithRetry executes (*OnMatchHook).Call() with a retry if it failes with spesific reasons
103+
func (h *OnMatchHook) CallWithRetry(body io.Reader, dataKind DataKind) (http.Header, error) {
104+
reqID := primitive.NewObjectID().String()
105+
// do 5 retries
106+
var headers http.Header
107+
var err error
108+
for i := 0; i < 5; i++ {
109+
// Is retry, do a backoff
110+
switch i {
111+
case 1:
112+
time.Sleep(time.Millisecond * 100)
113+
case 2:
114+
time.Sleep(time.Second)
115+
case 3:
116+
time.Sleep(time.Second * 2)
117+
case 4:
118+
time.Sleep(time.Second * 3)
119+
}
120+
121+
if body == nil {
122+
body = bytes.NewReader(nil)
123+
}
124+
headers, err = h.Call(body, dataKind, reqID)
125+
if err == nil {
126+
break
127+
}
128+
statusCodeErr, ok := err.(*StatusCodeError)
129+
if !ok {
130+
break
131+
}
132+
133+
retry := false
134+
switch statusCodeErr.code {
135+
case http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:
136+
retry = true
137+
}
138+
139+
if !retry {
140+
break
141+
}
142+
}
143+
return headers, err
144+
}
145+
146+
// StatusCodeError is an error thrown by (*OnMatchHook).Call() when the status code is >= 400
147+
type StatusCodeError struct {
148+
status string
149+
code int
150+
body []byte
151+
}
152+
153+
func (e *StatusCodeError) Error() string {
154+
if len(e.body) == 0 {
155+
return fmt.Sprintf("hook returned status code \"%s\" with a unreadable message", e.status)
156+
}
157+
return fmt.Sprintf("hook returned status code \"%s\" with message: %s", e.status, string(e.body))
158+
}
159+
100160
// Call calls the hook defined in OnMatchHook
101-
func (h *OnMatchHook) Call(body io.Reader, dataKind DataKind) (http.Header, error) {
161+
func (h *OnMatchHook) Call(body io.Reader, dataKind DataKind, reqID string) (http.Header, error) {
102162
req, err := http.NewRequest(h.Method, h.URL, body)
103163
if err != nil {
104164
return nil, err
105165
}
106166

107167
req.Header.Set("Content-Type", "application/json")
108168
req.Header.Set("User-Agent", "RT-CV")
169+
req.Header.Set("X-Request-ID", reqID)
109170

110171
contentTypeHeader, dataKindHeader := dataKind.contentTypeAndDataKind()
111172
req.Header.Set("Content-Type", contentTypeHeader)
@@ -123,12 +184,12 @@ func (h *OnMatchHook) Call(body io.Reader, dataKind DataKind) (http.Header, erro
123184
}
124185

125186
if resp.StatusCode >= 400 {
126-
respBody, err := ioutil.ReadAll(resp.Body)
127-
if err != nil {
128-
return req.Header, fmt.Errorf("hook returned status code \"%s\" with a unreadable message, error: %s", resp.Status, err.Error())
187+
respBody, _ := ioutil.ReadAll(resp.Body)
188+
return req.Header, &StatusCodeError{
189+
status: resp.Status,
190+
code: resp.StatusCode,
191+
body: respBody,
129192
}
130-
131-
return req.Header, fmt.Errorf("hook returned status code \"%s\" with message: %s", resp.Status, string(respBody))
132193
}
133194

134195
return req.Header, nil

0 commit comments

Comments
 (0)