Description
What version of Go are you using (go version
)?
$ go version go version go1.13.4 linux/amd64
Does this issue reproduce with the latest release?
I believe so.
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/data/home/jminter/.cache/go-build" GOENV="/data/home/jminter/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/data/home/jminter/go" GOPRIVATE="" GOPROXY="direct" GOROOT="/usr/lib/golang" GOSUMDB="off" GOTMPDIR="" GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build951550205=/tmp/go-build -gno-record-gcc-switches"
What did you do?
With a simple client like this, and using it to make a dumb GET request to some server:
c := &http.Client{
Timeout: 30 * time.Second,
}
If the server doesn't respond within 30 seconds, the client gets a timeout error as expected. But, Go does not close the underlying TCP connection to the server. If the client keeps retrying, unfortunately its subsequent requests get queued on the same TCP connection.
If the server doesn't close the connection, or perhaps if there's an underlying TCP connection (e.g. connectivity) break which means the client and server aren't actually communicating, the client doesn't redial to establish a new connection, at least perhaps until the local kernel socket send buffer is full, or there's a TCP keepalive timeout, or something (conjecture).
You can see this when it happens because netstat continues to show the connection, and you can see its local Recv-Q figure incrementing on each retry.
Setting DisableKeepAlives works around this problem, but it's a pretty big hammer.
IMO it would make more sense for Go to abandon and close the underlying TCP connection if an HTTP timeout has occured on it.
What did you expect to see?
I'd like to see Go net/http close underlying TCP connections if the HTTP request they are running times out.
What did you see instead?
It keeps the underlying TCP connection open.