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

net/http: http.Transport leaks context from the request #50798

Closed
juliens opened this issue Jan 25, 2022 · 13 comments · May be fixed by #50799
Closed

net/http: http.Transport leaks context from the request #50798

juliens opened this issue Jan 25, 2022 · 13 comments · May be fixed by #50799
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@juliens
Copy link
Contributor

juliens commented Jan 25, 2022

What version of Go are you using (go version)?

$ go version
go version go1.17 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/juliens/.cache/go-build"
GOENV="/home/juliens/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/juliens/dev/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/juliens/dev/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/juliens/.gvm/gos/go1.17"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/juliens/.gvm/gos/go1.17/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
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-build3799944390=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I use the http.Transport to make an HTTP request. I add a not empty struct in the request context.

You can reproduce it like this

What did you expect to see?

After the end of the request, the struct and the context are cleaned by the GC.

What did you see instead?

After the end of the request, the struct and the context are not cleaned by the GC.

Maybe this is the same issue as #43966

@gopherbot
Copy link

Change https://golang.org/cl/380674 mentions this issue: net/http: fix memory leak in http.Transport

@mknyszek
Copy link
Contributor

CC @neild

@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 25, 2022
@mknyszek mknyszek added this to the Backlog milestone Jan 25, 2022
@netrebel
Copy link

We seem to be having the same problem. In long running instances, memory utilization is going up every day reaching 80-90% in ~12 days. We started seeing this issue when we upgraded from Go 1.11.3 (no modules) to 1.17 (with go-modules), before the Memory would stay under 30%. Servers have 4GBs.

When I run pprof I see something similar to the comment in the ticket referenced:

      flat  flat%   sum%        cum   cum%
    1.50MB  5.07%  5.07%    12.04MB 40.70%  net/http.(*Transport).dialConn
         0     0%  5.07%    12.04MB 40.70%  net/http.(*Transport).dialConnFor
    8.03MB 27.14% 32.21%     8.03MB 27.14%  bufio.NewWriterSize (inline)
    6.02MB 20.36% 52.57%     6.02MB 20.36%  bufio.NewReaderSize (inline)

@luca147
Copy link

luca147 commented Dec 16, 2022

Same issue, there is some news about this?

@netrebel
Copy link

We seem to be having the same problem. In long running instances, memory utilization is going up every day reaching 80-90% in ~12 days. We started seeing this issue when we upgraded from Go 1.11.3 (no modules) to 1.17 (with go-modules), before the Memory would stay under 30%. Servers have 4GBs.

When I run pprof I see something similar to the comment in the ticket referenced:

      flat  flat%   sum%        cum   cum%
    1.50MB  5.07%  5.07%    12.04MB 40.70%  net/http.(*Transport).dialConn
         0     0%  5.07%    12.04MB 40.70%  net/http.(*Transport).dialConnFor
    8.03MB 27.14% 32.21%     8.03MB 27.14%  bufio.NewWriterSize (inline)
    6.02MB 20.36% 52.57%     6.02MB 20.36%  bufio.NewReaderSize (inline)

In case this helps someone, this other ticket was closed with a comment explaining how to avoid this issue: newrelic/go-agent#447 (comment)

@luca147
Copy link

luca147 commented Dec 17, 2022

Well I spotted in my case the solution, I have a global scope context that are aware when a term signal come and all of my workers need to be stopped when it comes. So every time a job start I create a new context with cancel and defer it. With this simple change the leak over bufio.NewWriterSize/bufio.NewReaderSize into transport disappear.

@urenner-n2i
Copy link

Any update on this issue?
When connecting to new hosts very often, the memory consumption increases very quickly, and never decreases.

@nikita21
Copy link

nikita21 commented Jun 5, 2023

We are seeing similar issue when we upgraded from Go 1.13 (no modules) to 1.17 (with go-modules). In our worker instances, memory utilization is going up every day reaching 80-90% in ~8 days. Even tried upgrading to 1.19 but issue still persists.

Screenshot 2023-06-05 at 3 42 50 PM

@xsteadfastx
Copy link

my pprof looks exactly the same. are there any workarounds? what can i do?

AlexanderYastrebov added a commit to AlexanderYastrebov/go that referenced this issue Jul 22, 2023
…dTrip

Clears wannConn ctx and prevents pending dialConnFor after connection delivered or canceled.

Fixes golang#50798
AlexanderYastrebov added a commit to AlexanderYastrebov/go that referenced this issue Jul 22, 2023
Clears wannConn ctx and prevents pending dialConnFor after connection delivered or canceled.

Fixes golang#50798
@gopherbot
Copy link

Change https://go.dev/cl/512196 mentions this issue: net/http: clear reference to the request context after transport getConn

@AlexanderYastrebov
Copy link
Contributor

Request context is used to dial connection and there is also #59017 (comment) that suggests that it should not be used for that.

@AlexanderYastrebov
Copy link
Contributor

AlexanderYastrebov commented Aug 3, 2023

#61524 only fixes request context leakage but does not fix *persistConn leaks (seen as bufio.NewWriterSize and bufio.NewReaderSize on the pictures) which I plan to address separately.

gopherbot pushed a commit that referenced this issue Aug 21, 2023
Clears wannConn ctx and prevents pending dialConnFor after connection delivered or canceled.

Updates #50798

Change-Id: I9a681ac0f222be56571fa768700220f6b5ee0888
GitHub-Last-Rev: fd6c83a
GitHub-Pull-Request: #61524
Reviewed-on: https://go-review.googlesource.com/c/go/+/512196
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
AlexanderYastrebov added a commit to AlexanderYastrebov/go that referenced this issue Aug 23, 2023
Transport getConn creates wantConn w, tries to obtain idle connection for it
based on the w.key and, when there is no idle connection, puts wantConn into
idleConnWait wantConnQueue.

Then getConn dials connection for w in a goroutine and blocks.
After dial succeeds getConn unblocks and returns connection to the caller.

At this point w is stored in the idleConnWait and will not be evicted
until another wantConn with the same w.key is requested or alive
connection returned into the idle pool which may not happen e.g. if
server closes the connection.

The problem is that even after tryDeliver succeeds w references
persistConn wrapper that allocates bufio.Reader and bufio.Writer and
prevents them from being garbage collected.

To fix the problem this change removes persistConn and error references
from wantConn and delivers them via channel to getConn.

This way wantConn could be kept in wantConnQueues arbitrary long.

Fixes golang#43966
Fixes golang#50798
AlexanderYastrebov added a commit to AlexanderYastrebov/go that referenced this issue Aug 23, 2023
Transport getConn creates wantConn w, tries to obtain idle connection for it
based on the w.key and, when there is no idle connection, puts wantConn into
idleConnWait wantConnQueue.

Then getConn dials connection for w in a goroutine and blocks.
After dial succeeds getConn unblocks and returns connection to the caller.

At this point w is stored in the idleConnWait and will not be evicted
until another wantConn with the same w.key is requested or alive
connection returned into the idle pool which may not happen e.g. if
server closes the connection.

The problem is that even after tryDeliver succeeds w references
persistConn wrapper that allocates bufio.Reader and bufio.Writer and
prevents them from being garbage collected.

To fix the problem this change removes persistConn and error references
from wantConn and delivers them via channel to getConn.

This way wantConn could be kept in wantConnQueues arbitrary long.

Fixes golang#43966
Fixes golang#50798
@gopherbot
Copy link

Change https://go.dev/cl/522095 mentions this issue: net/http: remove persistConn reference from wantConn

cellularmitosis pushed a commit to cellularmitosis/go that referenced this issue Aug 24, 2023
Clears wannConn ctx and prevents pending dialConnFor after connection delivered or canceled.

Updates golang#50798

Change-Id: I9a681ac0f222be56571fa768700220f6b5ee0888
GitHub-Last-Rev: fd6c83a
GitHub-Pull-Request: golang#61524
Reviewed-on: https://go-review.googlesource.com/c/go/+/512196
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
AlexanderYastrebov added a commit to AlexanderYastrebov/go that referenced this issue Mar 7, 2024
Transport getConn creates wantConn w, tries to obtain idle connection for it
based on the w.key and, when there is no idle connection, puts wantConn into
idleConnWait wantConnQueue.

Then getConn dials connection for w in a goroutine and blocks.
After dial succeeds getConn unblocks and returns connection to the caller.

At this point w is stored in the idleConnWait and will not be evicted
until another wantConn with the same w.key is requested or alive
connection returned into the idle pool which may not happen e.g. if
server closes the connection.

The problem is that even after tryDeliver succeeds w references
persistConn wrapper that allocates bufio.Reader and bufio.Writer and
prevents them from being garbage collected.

To fix the problem this change removes persistConn and error references
from wantConn and delivers them via channel to getConn.

This way wantConn could be kept in wantConnQueues arbitrary long.

Fixes golang#43966
Fixes golang#50798
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
9 participants