Skip to content

Commit 12cf297

Browse files
committed
Check for ctx.Err rather than context.DeadlineExceeded
1 parent 7f87980 commit 12cf297

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

bdns/dns.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,10 @@ func (c *impl) exchangeOne(ctx context.Context, hostname string, qtype uint16) (
228228
if err != nil {
229229
c.log.Infof("logDNSError chosenServer=[%s] hostname=[%s] queryType=[%s] err=[%s]", chosenServer, hostname, qtypeStr, err)
230230

231-
// Check if the error is a timeout error, which we want to retry.
232-
// Network errors that can timeout implement the net.Error interface.
231+
// Check if the error is a network timeout, rather than a local context
232+
// timeout. If it is, retry instead of giving up.
233233
var netErr net.Error
234-
isRetryable := errors.As(err, &netErr) && netErr.Timeout() && !errors.Is(err, context.DeadlineExceeded)
234+
isRetryable := ctx.Err() == nil && errors.As(err, &netErr) && netErr.Timeout()
235235
hasRetriesLeft := tries < c.maxTries
236236
if isRetryable && hasRetriesLeft {
237237
continue

bdns/dns_test.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,11 @@ type testExchanger struct {
608608

609609
var errTooManyRequests = errors.New("too many requests")
610610

611-
func (te *testExchanger) ExchangeContext(_ context.Context, m *dns.Msg, a string) (*dns.Msg, time.Duration, error) {
611+
func (te *testExchanger) ExchangeContext(ctx context.Context, m *dns.Msg, a string) (*dns.Msg, time.Duration, error) {
612+
if ctx.Err() != nil {
613+
return nil, 0, ctx.Err()
614+
}
615+
612616
te.Lock()
613617
defer te.Unlock()
614618
msg := &dns.Msg{
@@ -788,10 +792,16 @@ func TestRetryMetrics(t *testing.T) {
788792
staticProvider, err := NewStaticProvider([]string{dnsLoopbackAddr})
789793
test.AssertNotError(t, err, "Got error creating StaticProvider")
790794

795+
// This lookup should not be retried, because the error comes from the
796+
// context itself being cancelled. It should never see the error in the
797+
// testExchanger, because the fake exchanger (like the real http package)
798+
// checks for cancellation before doing any work.
791799
testClient := New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.UseMock(), tlsConfig)
792800
dr := testClient.(*impl)
793-
dr.exchanger = &testExchanger{errs: []error{context.Canceled}}
794-
_, _, err = dr.LookupTXT(t.Context(), "example.com")
801+
dr.exchanger = &testExchanger{errs: []error{errors.New("oops")}}
802+
ctx, cancel := context.WithCancel(t.Context())
803+
cancel()
804+
_, _, err = dr.LookupTXT(ctx, "example.com")
795805
if err == nil ||
796806
err.Error() != "DNS problem: query timed out (and was canceled) looking up TXT for example.com" {
797807
t.Errorf("expected %s, got %s", context.Canceled, err)
@@ -803,10 +813,14 @@ func TestRetryMetrics(t *testing.T) {
803813
"resolver": "127.0.0.1",
804814
}, 1)
805815

816+
// Same as above, except rather than cancelling the context ourselves, we
817+
// let the go runtime cancel it as a result of a deadline in the past.
806818
testClient = New(time.Second*10, staticProvider, metrics.NoopRegisterer, clock.NewFake(), 3, "", blog.UseMock(), tlsConfig)
807819
dr = testClient.(*impl)
808-
dr.exchanger = &testExchanger{errs: []error{context.DeadlineExceeded}}
809-
_, _, err = dr.LookupTXT(t.Context(), "example.com")
820+
dr.exchanger = &testExchanger{errs: []error{errors.New("oops")}}
821+
ctx, cancel = context.WithTimeout(t.Context(), -10*time.Hour)
822+
defer cancel()
823+
_, _, err = dr.LookupTXT(ctx, "example.com")
810824
if err == nil ||
811825
err.Error() != "DNS problem: query timed out looking up TXT for example.com" {
812826
t.Errorf("expected %s, got %s", context.DeadlineExceeded, err)

0 commit comments

Comments
 (0)