github.com/weaviate/weaviate@v1.24.6/adapters/clients/client.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package clients 13 14 import ( 15 "bytes" 16 "context" 17 "fmt" 18 "io" 19 "net/http" 20 "time" 21 ) 22 23 type retryClient struct { 24 client *http.Client 25 *retryer 26 } 27 28 func (c *retryClient) doWithCustomMarshaller(timeout time.Duration, 29 req *http.Request, body []byte, decode func([]byte) error, 30 ) (err error) { 31 ctx, cancel := context.WithTimeout(req.Context(), timeout) 32 defer cancel() 33 try := func(ctx context.Context) (bool, error) { 34 if body != nil { 35 req.Body = io.NopCloser(bytes.NewReader(body)) 36 } 37 res, err := c.client.Do(req) 38 if err != nil { 39 return ctx.Err() == nil, fmt.Errorf("connect: %w", err) 40 } 41 42 respBody, err := io.ReadAll(res.Body) 43 if err != nil { 44 return shouldRetry(res.StatusCode), fmt.Errorf("read response: %w", err) 45 } 46 defer res.Body.Close() 47 48 if code := res.StatusCode; code != http.StatusOK { 49 return shouldRetry(code), fmt.Errorf("status code: %v, error: %s", code, respBody) 50 } 51 52 if err := decode(respBody); err != nil { 53 return false, fmt.Errorf("unmarshal response: %w", err) 54 } 55 56 return false, nil 57 } 58 return c.retry(ctx, 9, try) 59 } 60 61 type retryer struct { 62 minBackOff time.Duration 63 maxBackOff time.Duration 64 timeoutUnit time.Duration 65 } 66 67 func newRetryer() *retryer { 68 return &retryer{ 69 minBackOff: time.Millisecond * 250, 70 maxBackOff: time.Second * 30, 71 timeoutUnit: time.Second, // used by unit tests 72 } 73 } 74 75 func (r *retryer) retry(ctx context.Context, n int, work func(context.Context) (bool, error)) error { 76 delay := r.minBackOff 77 for { 78 keepTrying, err := work(ctx) 79 if !keepTrying || n < 1 || err == nil { 80 return err 81 } 82 83 n-- 84 if delay = backOff(delay); delay > r.maxBackOff { 85 delay = r.maxBackOff 86 } 87 timer := time.NewTimer(delay) 88 select { 89 case <-ctx.Done(): 90 timer.Stop() 91 return fmt.Errorf("%v: %w", err, ctx.Err()) 92 case <-timer.C: 93 } 94 timer.Stop() 95 } 96 }