RoundTrip implements http.RoundTripper
(req Request)
| 46 | |
| 47 | // RoundTrip implements http.RoundTripper |
| 48 | func (r retryWare) RoundTrip(req Request) (*http.Response, error) { |
| 49 | ctx := req.Context() |
| 50 | ctx, span := tracer.Start(ctx, "frontend.Retry", trace.WithSpanKind(trace.SpanKindClient)) |
| 51 | defer span.End() |
| 52 | |
| 53 | // context propagation |
| 54 | req.SetContext(ctx) |
| 55 | |
| 56 | tries := 0 |
| 57 | defer func() { r.retriesCount.Observe(float64(tries)) }() |
| 58 | |
| 59 | for { |
| 60 | if ctx.Err() != nil { |
| 61 | return nil, ctx.Err() |
| 62 | } |
| 63 | |
| 64 | resp, err := r.next.RoundTrip(req) |
| 65 | |
| 66 | if ctx.Err() != nil { |
| 67 | return nil, ctx.Err() |
| 68 | } |
| 69 | |
| 70 | if r.maxRetries == 0 { |
| 71 | return resp, err |
| 72 | } |
| 73 | |
| 74 | // do not retry if no error and response is not HTTP 5xx |
| 75 | if err == nil && resp != nil && !shouldRetry(resp.StatusCode) { |
| 76 | return resp, nil |
| 77 | } |
| 78 | |
| 79 | /* ---- HTTP GRPC translation ---- */ |
| 80 | // the following 2 blocks translate httpgrpc errors into something |
| 81 | // the rest of the http pipeline can understand. these really should |
| 82 | // be there own pipeline item independent of the retry middleware, but |
| 83 | // they've always been here and for safety we're not moving them right now. |
| 84 | if errors.Is(err, queue.ErrTooManyRequests) { |
| 85 | return &http.Response{ |
| 86 | StatusCode: http.StatusTooManyRequests, |
| 87 | Status: http.StatusText(http.StatusTooManyRequests), |
| 88 | Body: io.NopCloser(strings.NewReader("job queue full")), |
| 89 | }, nil |
| 90 | } |
| 91 | |
| 92 | // do not retry if GRPC error contains response that is not HTTP 5xx |
| 93 | httpResp, ok := httpgrpc.HTTPResponseFromError(err) |
| 94 | if ok && !shouldRetry(int(httpResp.Code)) { |
| 95 | return resp, err |
| 96 | } |
| 97 | /* ---- HTTP GRPC translation ---- */ |
| 98 | |
| 99 | // reached max retries |
| 100 | tries++ |
| 101 | if tries >= r.maxRetries { |
| 102 | return resp, err |
| 103 | } |
| 104 | |
| 105 | // retries have their weight bumped. a common retry reason is the request was simply too large to process |
nothing calls this directly
no test coverage detected