net: implement query-response fast failover in builtin dns stub resolver
Speed improvements via code cleanup, and changes to make go dns behave more like glibc resolver. See https://groups.google.com/forum/#!topic/golang-dev/lV-0aHqxVeo
Fixes #6579.
Benchmark results on linux/amd64
benchmark old ns/op new ns/op delta
BenchmarkGoLookupIP 4831903 2572937 -46.75%
BenchmarkGoLookupIPNoSuchHost 10114105 2419641 -76.08%
BenchmarkGoLookupIPWithBrokenNameServer 20007735624 5004490730 -74.99%
benchmark old allocs new allocs delta
BenchmarkGoLookupIP 287 288 0.35%
BenchmarkGoLookupIPNoSuchHost 204 102 -50.00%
BenchmarkGoLookupIPWithBrokenNameServer 410 358 -12.68%
benchmark old bytes new bytes delta
BenchmarkGoLookupIP 13181 13271 0.68%
BenchmarkGoLookupIPNoSuchHost 17260 8714 -49.51%
BenchmarkGoLookupIPWithBrokenNameServer 28160 22432 -20.34%
LGTM=mikioh.mikioh
R=golang-codereviews, mikioh.mikioh, bradfitz, josharian, abursavich
CC=golang-codereviews
https://golang.org/cl/128820043
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index dc5eed9..abe7da0 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -169,33 +169,20 @@
}
timeout := time.Duration(cfg.timeout) * time.Second
var lastErr error
- for _, server := range cfg.servers {
- server += ":53"
- lastErr = &DNSError{
- Err: "no answer from DNS server",
- Name: name,
- Server: server,
- IsTimeout: true,
- }
- for i := 0; i < cfg.attempts; i++ {
+ for i := 0; i < cfg.attempts; i++ {
+ for _, server := range cfg.servers {
+ server = JoinHostPort(server, "53")
msg, err := exchange(server, name, qtype, timeout)
if err != nil {
- if nerr, ok := err.(Error); ok && nerr.Timeout() {
- lastErr = &DNSError{
- Err: nerr.Error(),
- Name: name,
- Server: server,
- IsTimeout: true,
- }
- continue
-
- }
lastErr = &DNSError{
Err: err.Error(),
Name: name,
Server: server,
}
- break
+ if nerr, ok := err.(Error); ok && nerr.Timeout() {
+ lastErr.(*DNSError).IsTimeout = true
+ }
+ continue
}
cname, addrs, err := answer(name, server, msg, qtype)
if err == nil || err.(*DNSError).Err == noSuchHost {
@@ -387,31 +374,36 @@
return
}
}
- var records []dnsRR
- var cname string
- var err4, err6 error
- cname, records, err4 = lookup(name, dnsTypeA)
- addrs = convertRR_A(records)
- if cname != "" {
- name = cname
+ type racer struct {
+ qtype uint16
+ rrs []dnsRR
+ error
}
- _, records, err6 = lookup(name, dnsTypeAAAA)
- if err4 != nil && err6 == nil {
- // Ignore A error because AAAA lookup succeeded.
- err4 = nil
+ lane := make(chan racer, 1)
+ qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
+ for _, qtype := range qtypes {
+ go func(qtype uint16) {
+ _, rrs, err := lookup(name, qtype)
+ lane <- racer{qtype, rrs, err}
+ }(qtype)
}
- if err6 != nil && len(addrs) > 0 {
- // Ignore AAAA error because A lookup succeeded.
- err6 = nil
+ var lastErr error
+ for range qtypes {
+ racer := <-lane
+ if racer.error != nil {
+ lastErr = racer.error
+ continue
+ }
+ switch racer.qtype {
+ case dnsTypeA:
+ addrs = append(addrs, convertRR_A(racer.rrs)...)
+ case dnsTypeAAAA:
+ addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
+ }
}
- if err4 != nil {
- return nil, err4
+ if len(addrs) == 0 && lastErr != nil {
+ return nil, lastErr
}
- if err6 != nil {
- return nil, err6
- }
-
- addrs = append(addrs, convertRR_AAAA(records)...)
return addrs, nil
}