]> Cypherpunks repositories - gostls13.git/commitdiff
net: implement query-response fast failover in builtin dns stub resolver
authorAlex A Skinner <alex@lx.lc>
Sat, 30 Aug 2014 04:12:28 +0000 (13:12 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Sat, 30 Aug 2014 04:12:28 +0000 (13:12 +0900)
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

src/pkg/net/dnsclient_unix.go
src/pkg/net/dnsclient_unix_test.go

index dc5eed96cc605952ba7272883431f2f840f68ead..abe7da05cb579e14fd27f17782528190224f0487 100644 (file)
@@ -169,33 +169,20 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, err
        }
        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 @@ func goLookupIP(name string) (addrs []IP, err error) {
                        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
-       }
-       if err4 != nil {
-               return nil, err4
+       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 err6 != nil {
-               return nil, err6
+       if len(addrs) == 0 && lastErr != nil {
+               return nil, lastErr
        }
-
-       addrs = append(addrs, convertRR_AAAA(records)...)
        return addrs, nil
 }
 
index 204c8b7ef7eaf73aec33f17cb7fc1c7c90467821..bd7129bd13230090810f2ba42ced7c5017961232 100644 (file)
@@ -218,8 +218,29 @@ func TestReloadResolvConfChange(t *testing.T) {
        r.WantServers([]string{"[8.8.4.4]"})
 }
 
+func BenchmarkGoLookupIP(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               goLookupIP("www.example.com")
+       }
+}
+
 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
        for i := 0; i < b.N; i++ {
                goLookupIP("some.nonexistent")
        }
 }
+
+func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
+       onceLoadConfig.Do(loadDefaultConfig)
+       if cfg.dnserr != nil || cfg.dnsConfig == nil {
+               b.Fatalf("loadConfig failed: %v", cfg.dnserr)
+       }
+       // This looks ugly but it's safe as long as benchmarks are run
+       // sequentially in package testing.
+       orig := cfg.dnsConfig
+       cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
+       for i := 0; i < b.N; i++ {
+               goLookupIP("www.example.com")
+       }
+       cfg.dnsConfig = orig
+}