LookupHost taking up to 5 seconds to resolve localhost

266 views
Skip to first unread message

Peter Waller

unread,
Oct 27, 2016, 6:47:21 AM10/27/16
to golang-nuts
Hi Nuts,

net.LookupHost is taking a long time (up to 5 seconds) to resolve localhost, where I expect it to be instant. At other times it can take anywhere from 1-100ms, but it seems to reliably return a result shortly after 5 seconds.

The details:

* I'm doing local development inside a docker container (alpine linux: `docker run alpine sh`). Outside of this environment I can't seem to easily get the problem to manifest, but I haven't tried hard.

* The problem manifests as many things having an additional random amount of latency of order 100ms-5s.

* Inside the container, various services are talking to each other through URLs which read `http://localhost:...`

* Resolv.conf has `nameserver 8.8.8.8` which may or may not be unreachable at different times. If it is unreachable, then it takes 5 seconds to resolve localhost.

* I'm on a slow mobile network, so I hit this frequently.

* I experience 5 second timeouts even on fast networks where the resolver should be reachable, though this could perhaps be explained by an out-of-date resolv.conf inside the docker container.

* Tweaking GODEBUG=netdns= to either go or cgo seems to fix the problem.

* The presence or absense of an empty /etc/nsswitch.conf has no effect.

* An empty resolv.conf still selects "dns,files" but then returns a result fast (in microseconds).

My test program:

func main() {
    start := time.Now()
    _, err := net.LookupHost("localhost")
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("Took %v", time.Since(start))
}

This seems to be the fundamental problem:

# GODEBUG=netdns=2 ./tmp-1
go package net: dynamic selection of DNS resolver
go package net: hostLookupOrder(localhost) = dns,files
Took 351.143424ms

Why is go choosing "dns,files"? If I choose either cgo or go as my resolver (see end of mail), it correctly chooses `files,dns` then everything goes quickly, taking microseconds rather than hundreds of milliseconds.

On a Debian host, it seems to correctly select `files,dns`.

Am I missing something?

Also, in the interests of trimming latency, is there any reason the Go resolver shouldn't just directly return 127.0.0.1 as a hard-coded result for "localhost"? I understood it may be required by the RFC, but the strongest language I could find was in RFC2606:


      The ".localhost" TLD has traditionally been statically defined in
      host DNS implementations as having an A record pointing to the
      loop back IP address and is reserved for such use.  Any other use
      would conflict with widely deployed code which assumes this use.


Thanks,

- Peter

Selecting either resolver has the correct behaviour, compared with that shown above:

# GODEBUG=netdns=go+2 ./tmp-1
go package net: GODEBUG setting forcing use of Go's resolver
go package net: hostLookupOrder(localhost) = files,dns
Took 376.164µs

# GODEBUG=netdns=cgo+2 ./tmp-1
go package net: using cgo DNS resolver
go package net: hostLookupOrder(localhost) = cgo
Took 102.334µs

Reply all
Reply to author
Forward
0 new messages