Skip to content

Commit 6462d8b

Browse files
Anaethelionaxw
andauthored
estransport: honour request context cancellation (#301) (#316)
Co-authored-by: Laurent Saint-Félix <laurent.saintfelix@elastic.co> Co-authored-by: Andrew Wilkins <axw@elastic.co>
1 parent 99d60b7 commit 6462d8b

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

estransport/estransport.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ const (
4848
)
4949

5050
var (
51-
userAgent string
52-
metaHeader string
51+
userAgent string
52+
metaHeader string
5353
compatibilityHeader bool
54-
reGoVersion = regexp.MustCompile(`go(\d+\.\d+\..+)`)
54+
reGoVersion = regexp.MustCompile(`go(\d+\.\d+\..+)`)
5555

5656
defaultMaxRetries = 3
5757
defaultRetryOnStatus = [...]int{502, 503, 504}
@@ -396,7 +396,19 @@ func (c *Client) Perform(req *http.Request) (*http.Response, error) {
396396

397397
// Delay the retry if a backoff function is configured
398398
if c.retryBackoff != nil {
399-
time.Sleep(c.retryBackoff(i + 1))
399+
var cancelled bool
400+
backoff := c.retryBackoff(i + 1)
401+
timer := time.NewTimer(backoff)
402+
select {
403+
case <-req.Context().Done():
404+
err = req.Context().Err()
405+
cancelled = true
406+
timer.Stop()
407+
case <-timer.C:
408+
}
409+
if cancelled {
410+
break
411+
}
400412
}
401413
}
402414

estransport/estransport_internal_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package estransport
2222
import (
2323
"bytes"
2424
"compress/gzip"
25+
"context"
2526
"fmt"
2627
"io"
2728
"io/ioutil"
@@ -785,6 +786,37 @@ func TestTransportPerformRetries(t *testing.T) {
785786
t.Errorf("Unexpected duration, want=>%s, got=%s", expectedDuration, end)
786787
}
787788
})
789+
790+
t.Run("Delay the retry with retry on timeout and context deadline", func(t *testing.T) {
791+
var i int
792+
u, _ := url.Parse("http://foo.bar")
793+
tp, _ := New(Config{
794+
EnableRetryOnTimeout: true,
795+
MaxRetries: 100,
796+
RetryBackoff: func(i int) time.Duration { return time.Hour },
797+
URLs: []*url.URL{u},
798+
Transport: &mockTransp{
799+
RoundTripFunc: func(req *http.Request) (*http.Response, error) {
800+
i++
801+
<-req.Context().Done()
802+
return nil, req.Context().Err()
803+
},
804+
},
805+
})
806+
807+
req, _ := http.NewRequest("GET", "/abc", nil)
808+
ctx, cancel := context.WithTimeout(req.Context(), 50*time.Millisecond)
809+
defer cancel()
810+
req = req.WithContext(ctx)
811+
812+
_, err := tp.Perform(req)
813+
if err != context.DeadlineExceeded {
814+
t.Fatalf("expected context.DeadlineExceeded, got %s", err)
815+
}
816+
if i != 1 {
817+
t.Fatalf("unexpected number of requests: expected 1, got got %d", i)
818+
}
819+
})
788820
}
789821

790822
func TestURLs(t *testing.T) {
@@ -937,19 +969,19 @@ func TestCompatibilityHeader(t *testing.T) {
937969
{
938970
name: "Compatibility header disabled",
939971
compatibilityHeader: false,
940-
bodyPresent: false,
972+
bodyPresent: false,
941973
expectsHeader: []string{"application/json"},
942974
},
943975
{
944976
name: "Compatibility header enabled",
945977
compatibilityHeader: true,
946-
bodyPresent: false,
978+
bodyPresent: false,
947979
expectsHeader: []string{"application/vnd.elasticsearch+json;compatible-with=7"},
948980
},
949981
{
950-
name: "Compatibility header enabled with body",
982+
name: "Compatibility header enabled with body",
951983
compatibilityHeader: true,
952-
bodyPresent: true,
984+
bodyPresent: true,
953985
expectsHeader: []string{"application/vnd.elasticsearch+json;compatible-with=7"},
954986
},
955987
}

0 commit comments

Comments
 (0)