Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove SingleInflight support from Client #1454

Merged
merged 2 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 15 additions & 29 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"context"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"net"
"strings"
Expand Down Expand Up @@ -56,14 +55,20 @@ type Client struct {
// Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
// WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
// Client.Dialer) or context.Context.Deadline (see ExchangeContext)
Timeout time.Duration
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight
Timeout time.Duration
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.

// SingleInflight previously serialised multiple concurrent queries for the
// same Qname, Qtype and Qclass to ensure only one would be in flight at a
// time.
//
// Deprecated: This is a no-op. Callers should implement their own in flight
// query caching.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe link to the issue for some more background on why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will go doc properly linkify it in the form I've added? Do you know?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, we have both forms already:

reverse.go:	// Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733.
dns_test.go:	tkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577

except these are not part of the public docs.

I thought you can make pkg.go.dev look at a specific commit but my go gets fail with unknown revision, either way, folks can find the issue regardless of this being linkified

SingleInflight bool
}

// Exchange performs a synchronous UDP query. It sends the message m to the address
Expand Down Expand Up @@ -185,26 +190,7 @@ func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration
return c.exchangeWithConnContext(context.Background(), m, conn)
}

func (c *Client) exchangeWithConnContext(ctx context.Context, m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {
if !c.SingleInflight {
return c.exchangeContext(ctx, m, conn)
}

q := m.Question[0]
key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
// When we're doing singleflight we don't want one context cancellation, cancel _all_ outstanding queries.
// Hence we ignore the context and use Background().
return c.exchangeContext(context.Background(), m, conn)
})
if r != nil && shared {
r = r.Copy()
}

return r, rtt, err
}

func (c *Client) exchangeContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
func (c *Client) exchangeWithConnContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
opt := m.IsEdns0()
// If EDNS0 is used use that for size.
if opt != nil && opt.UDPSize() >= MinMsgSize {
Expand Down
52 changes: 0 additions & 52 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,58 +679,6 @@ func TestTimeout(t *testing.T) {
})
}

// Check that responses from deduplicated requests aren't shared between callers
func TestConcurrentExchanges(t *testing.T) {
cases := make([]*Msg, 2)
cases[0] = new(Msg)
cases[1] = new(Msg)
cases[1].Truncated = true

for _, m := range cases {
mm := m // redeclare m so as not to trip the race detector
handler := func(w ResponseWriter, req *Msg) {
r := mm.Copy()
r.SetReply(req)

w.WriteMsg(r)
}

HandleFunc("miek.nl.", handler)
defer HandleRemove("miek.nl.")

s, addrstr, _, err := RunLocalUDPServer(":0")
if err != nil {
t.Fatalf("unable to run test server: %s", err)
}
defer s.Shutdown()

m := new(Msg)
m.SetQuestion("miek.nl.", TypeSRV)

c := &Client{
SingleInflight: true,
}
// Force this client to always return the same request,
// even though we're querying sequentially. Running the
// Exchange calls below concurrently can fail due to
// goroutine scheduling, but this simulates the same
// outcome.
c.group.dontDeleteForTesting = true

r := make([]*Msg, 2)
for i := range r {
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
if r[i] == nil {
t.Errorf("response %d is nil", i)
}
}

if r[0] == r[1] {
t.Errorf("got same response, expected non-shared responses")
}
}
}

func TestExchangeWithConn(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
Expand Down
61 changes: 0 additions & 61 deletions singleinflight.go

This file was deleted.