Skip to content

Commit

Permalink
Add IsHedgedRequest (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg authored Nov 30, 2021
1 parent 95e4a00 commit 9e940c5
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
15 changes: 13 additions & 2 deletions hedged.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (ht *hedgedTransport) RoundTrip(req *http.Request) (*http.Response, error)
for sent := 0; len(errOverall.Errors) < ht.upto; sent++ {
if sent < ht.upto {
idx := sent
subReq, cancel := reqWithCtx(req, mainCtx)
subReq, cancel := reqWithCtx(req, mainCtx, idx != 0)
cancels[idx] = cancel

runInPool(func() {
Expand Down Expand Up @@ -174,12 +174,23 @@ type indexedResp struct {
Resp *http.Response
}

func reqWithCtx(r *http.Request, ctx context.Context) (*http.Request, func()) {
func reqWithCtx(r *http.Request, ctx context.Context, isHedged bool) (*http.Request, func()) {
ctx, cancel := context.WithCancel(ctx)
if isHedged {
ctx = context.WithValue(ctx, hedgedRequest{}, struct{}{})
}
req := r.WithContext(ctx)
return req, cancel
}

type hedgedRequest struct{}

// IsHedgedRequest reports when a request is hedged.
func IsHedgedRequest(r *http.Request) bool {
val := r.Context().Value(hedgedRequest{})
return val != nil
}

// atomicCounter is a false sharing safe counter.
type atomicCounter struct {
count uint64
Expand Down
37 changes: 37 additions & 0 deletions hedged_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package hedgedhttp_test
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -425,6 +426,42 @@ func TestCancelByClient(t *testing.T) {
}
}

func TestIsHedged(t *testing.T) {
var gotRequests int

rt := testRoundTripper(func(req *http.Request) (*http.Response, error) {
if gotRequests == 0 {
if hedgedhttp.IsHedgedRequest(req) {
t.Fatal("first request is hedged")
}
} else {
if !hedgedhttp.IsHedgedRequest(req) {
t.Fatalf("%d request is not hedged", gotRequests)
}
}
gotRequests++
return nil, errors.New("just an error")
})

req, err := http.NewRequest("GET", "http://no-matter-what", http.NoBody)
if err != nil {
t.Fatal(err)
}

const upto = 7
_, _ = hedgedhttp.NewRoundTripper(10*time.Millisecond, upto, rt).RoundTrip(req)

if gotRequests != upto {
t.Fatalf("want %v, got %v", upto, gotRequests)
}
}

type testRoundTripper func(req *http.Request) (*http.Response, error)

func (t testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return t(req)
}

func checkAllMetricsAreZero(t *testing.T, metrics *hedgedhttp.Stats) {
expectExactMetricsAndSnapshot(t, metrics, hedgedhttp.StatsSnapshot{})
}
Expand Down

0 comments on commit 9e940c5

Please sign in to comment.