uber-go / goleak

Goroutine leak detector
MIT License
4.48k stars 148 forks source link

[question]I'm literally confused about the result, why the test failed? #102

Closed guest6379 closed 1 year ago

guest6379 commented 1 year ago

os platform: ubuntu 20.04 go version: 1.19.7

Can someone advise me how to pass the test? Is the problem caused by the running time FAIL demo/test 1.169s?

package test

import (
    "fmt"
    "net/http"
    "testing"

    "go.uber.org/goleak"
)

func TestXxx(t *testing.T) {
    defer goleak.VerifyNone(t)

    resp, err := http.Get("https://www.microsoft.com")
    if err != nil {
        t.Fail()
    }
        defer resp.Body.Close()
    fmt.Println(resp.StatusCode)
}

OUTPUT:

go test 
200
--- FAIL: TestXxx (1.16s)
    m_test.go:19: found unexpected goroutines:
        [Goroutine 13 in state IO wait, with internal/poll.runtime_pollWait on top of the stack:
        goroutine 13 [IO wait]:
        internal/poll.runtime_pollWait(0x7f5fc173dff0, 0x72)
                /home/ubuntu/go/src/runtime/netpoll.go:305 +0x89
        internal/poll.(*pollDesc).wait(0xc0003b0480?, 0xc000284a00?, 0x0)
                /home/ubuntu/go/src/internal/poll/fd_poll_runtime.go:84 +0x32
        internal/poll.(*pollDesc).waitRead(...)
                /home/ubuntu/go/src/internal/poll/fd_poll_runtime.go:89
        internal/poll.(*FD).Read(0xc0003b0480, {0xc000284a00, 0x2500, 0x2500})
                /home/ubuntu/go/src/internal/poll/fd_unix.go:167 +0x25a
        net.(*netFD).Read(0xc0003b0480, {0xc000284a00?, 0xc00040ad60?, 0xc000285409?})
                /home/ubuntu/go/src/net/fd_posix.go:55 +0x29
        net.(*conn).Read(0xc000014008, {0xc000284a00?, 0x56?, 0x0?})
                /home/ubuntu/go/src/net/net.go:183 +0x45
        crypto/tls.(*atLeastReader).Read(0xc0002d0018, {0xc000284a00?, 0xc0003e1520?, 0x4e12a8?})
                /home/ubuntu/go/src/crypto/tls/conn.go:787 +0x3d
        bytes.(*Buffer).ReadFrom(0xc000798cf8, {0xb063e0, 0xc0002d0018})
                /home/ubuntu/go/src/bytes/buffer.go:202 +0x98
        crypto/tls.(*Conn).readFromUntil(0xc000798a80, {0xb06700?, 0xc000014008}, 0x1afc?)
                /home/ubuntu/go/src/crypto/tls/conn.go:809 +0xe5
        crypto/tls.(*Conn).readRecordOrCCS(0xc000798a80, 0x0)
                /home/ubuntu/go/src/crypto/tls/conn.go:616 +0x116
        crypto/tls.(*Conn).readRecord(...)
                /home/ubuntu/go/src/crypto/tls/conn.go:582
        crypto/tls.(*Conn).Read(0xc000798a80, {0xc00028f000, 0x1000, 0x670a80?})
                /home/ubuntu/go/src/crypto/tls/conn.go:1315 +0x16f
        bufio.(*Reader).Read(0xc0000aa720, {0xc0001de2e0, 0x9, 0x67e4a5?})
                /home/ubuntu/go/src/bufio/bufio.go:237 +0x1bb
        io.ReadAtLeast({0xb062e0, 0xc0000aa720}, {0xc0001de2e0, 0x9, 0x9}, 0x9)
                /home/ubuntu/go/src/io/io.go:332 +0x9a
        io.ReadFull(...)
                /home/ubuntu/go/src/io/io.go:351
        net/http.http2readFrameHeader({0xc0001de2e0?, 0x9?, 0xc0002ae060?}, {0xb062e0?, 0xc0000aa720?})
                /home/ubuntu/go/src/net/http/h2_bundle.go:1565 +0x6e
        net/http.(*http2Framer).ReadFrame(0xc0001de2a0)
                /home/ubuntu/go/src/net/http/h2_bundle.go:1829 +0x95
        net/http.(*http2clientConnReadLoop).run(0xc000063f98)
                /home/ubuntu/go/src/net/http/h2_bundle.go:8874 +0x130
        net/http.(*http2ClientConn).readLoop(0xc00017ed80)
                /home/ubuntu/go/src/net/http/h2_bundle.go:8770 +0x6f
        created by net/http.(*http2Transport).newClientConn
                /home/ubuntu/go/src/net/http/h2_bundle.go:7477 +0xaaa
        ]
FAIL
exit status 1
FAIL    demo/test       1.169s
prashantv commented 1 year ago

There's a couple of things needed to avoid the leak:

Updated test code that doesn't leak:

func TestXxx(t *testing.T) {
        defer goleak.VerifyNone(t)

        resp, err := http.Get("https://www.microsoft.com")
        if err != nil {
                t.Fail()
        }
        defer resp.Body.Close()
        defer http.DefaultClient.CloseIdleConnections()

        fmt.Println(resp.StatusCode)
        ioutil.ReadAll(resp.Body)
}
guest6379 commented 1 year ago

There's a couple of things needed to avoid the leak:

* Read the response body. This shouldn't be needed as the body is closed, but I saw failures unless the body was read, and I think it might be related to: [x/net/http2: RoundTrip and/or CloseIdleConnections may leave connections in use after returning golang/go#50027](https://github.com/golang/go/issues/50027)

* Call [`http.DefaultClient.CloseIdleConnections`](https://pkg.go.dev/net/http#Client.CloseIdleConnections) to close idle connections.

Updated test code that doesn't leak:

func TestXxx(t *testing.T) {
        defer goleak.VerifyNone(t)

        resp, err := http.Get("https://www.microsoft.com")
        if err != nil {
                t.Fail()
        }
        defer resp.Body.Close()
        defer http.DefaultClient.CloseIdleConnections()

        fmt.Println(resp.StatusCode)
        ioutil.ReadAll(resp.Body)
}

Thank you so much for the explanatory code snippet, have a nice day