Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e344522c63 |
@@ -30,7 +30,8 @@ const (
|
|||||||
// rate limit that names no wait, waits a jittered exponential backoff. The Request's Timeout, or DefaultTimeout, is one
|
// rate limit that names no wait, waits a jittered exponential backoff. The Request's Timeout, or DefaultTimeout, is one
|
||||||
// deadline for the whole call, retries and waits included; a caller's
|
// deadline for the whole call, retries and waits included; a caller's
|
||||||
// earlier context deadline wins. A retry whose wait would not end before
|
// earlier context deadline wins. A retry whose wait would not end before
|
||||||
// that deadline is not made: the last error is returned at once.
|
// that deadline is not made: the last error is returned at once. A wait
|
||||||
|
// cut short by cancellation or the deadline returns the context's error.
|
||||||
// ListModels and Verify pass straight through.
|
// ListModels and Verify pass straight through.
|
||||||
type Retrying struct {
|
type Retrying struct {
|
||||||
Provider
|
Provider
|
||||||
@@ -83,8 +84,8 @@ func (r *Retrying) Generate(ctx context.Context, req Request) (Response, error)
|
|||||||
if !r.now().Add(wait).Before(deadline) {
|
if !r.now().Add(wait).Before(deadline) {
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
if serr := r.sleep(ctx, wait); serr != nil {
|
if err := r.sleep(ctx, wait); err != nil {
|
||||||
return resp, err
|
return Response{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,6 +240,43 @@ func TestRetryingStopsWhenContextDone(t *testing.T) {
|
|||||||
assertConsumed(t, f, 1)
|
assertConsumed(t, f, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRetryingWaitInterruptedReturnsContextError(t *testing.T) {
|
||||||
|
t.Run("cancelled", func(t *testing.T) {
|
||||||
|
r, f, _ := newTestRetrying()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
r.sleep = func(ctx context.Context, d time.Duration) error {
|
||||||
|
cancel()
|
||||||
|
return sleep(ctx, d)
|
||||||
|
}
|
||||||
|
f.QueueGenerate(fail(NewRateLimitedError(3*time.Second, "")))
|
||||||
|
|
||||||
|
_, err := r.Generate(ctx, retryReq)
|
||||||
|
if !errors.Is(err, context.Canceled) || errors.Is(err, ErrRateLimited) {
|
||||||
|
t.Fatalf("err = %v, want context.Canceled", err)
|
||||||
|
}
|
||||||
|
assertConsumed(t, f, 1)
|
||||||
|
})
|
||||||
|
t.Run("deadline", func(t *testing.T) {
|
||||||
|
r, f, _ := newTestRetrying()
|
||||||
|
// The fake clock stands still, so the 500ms backoff fits before
|
||||||
|
// the caller's deadline; the real wait outlasts it.
|
||||||
|
r.jitter = func(time.Duration) time.Duration { return 0 }
|
||||||
|
r.sleep = func(ctx context.Context, _ time.Duration) error {
|
||||||
|
return sleep(ctx, time.Hour)
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 600*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
f.QueueGenerate(fail(NewRateLimitedError(0, "")))
|
||||||
|
|
||||||
|
_, err := r.Generate(ctx, retryReq)
|
||||||
|
if !errors.Is(err, context.DeadlineExceeded) || errors.Is(err, ErrRateLimited) {
|
||||||
|
t.Fatalf("err = %v, want context.DeadlineExceeded", err)
|
||||||
|
}
|
||||||
|
assertConsumed(t, f, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// slowProvider is a Provider whose every attempt takes d on clock c,
|
// slowProvider is a Provider whose every attempt takes d on clock c,
|
||||||
// records the deadline of the context it received, and times out.
|
// records the deadline of the context it received, and times out.
|
||||||
type slowProvider struct {
|
type slowProvider struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user