-
Notifications
You must be signed in to change notification settings - Fork 617
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
The client does not reuse HTTP connections #123
Comments
Hey, yes, the connections should definitely be reused (assuming the body is closed). There's this test:
which essentially runs what you have in the example, and uses the nodes stats in Elasticsearch to check that the number of connections is not growing — did you try to run that as well? |
Hi, thanks for the quick reply (and reformatting my message too :)). Yes, that's the test mentioned in #118, but it's asserting a slightly different thing (that number of open connections isn't growing). And yes, it's behaving in the same way: damjan@iMac ~/Downloads/go-elasticsearch-master go test elasticsearch_integration_test.go
ok command-line-arguments 1.335s
damjan@iMac ~/Downloads/go-elasticsearch-master netstat -an | grep 9200 | awk '{print $6}' | sort | uniq -c
2 LISTEN
132 TIME_WAIT Hope this helps, |
It seems like closing HTTP response body without consuming the response is causing a connection to be closed by go HTTP stack. So invoking res.String(), just for the side effect, makes the issue go away. Which is obviously a workaround, but quite silly... package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
)
func main() {
es, err := elasticsearch.NewClient(elasticsearch.Config{
Addresses: []string{"http://localhost:9200"},
//Transport: &http.Transport{MaxIdleConns: 8, MaxIdleConnsPerHost: 8, MaxConnsPerHost: 16, IdleConnTimeout: 10 * time.Second},
})
if err != nil {
panic(err)
}
for i := 0; i < 1000; i++ {
func() {
log.Printf("Saving %d\n", i)
req := esapi.IndexRequest{
Index: "test",
DocumentID: fmt.Sprintf("%d", i),
Body: strings.NewReader(`{}`),
Refresh: "true",
}
res, err := req.Do(context.Background(), es)
if err != nil {
log.Println(err)
}
defer func() {
log.Println("Closing")
err := res.Body.Close()
if err != nil {
log.Println(err)
}
}()
res.String()
if res.IsError() {
log.Println(res.Status())
}
}()
}
} This now produces (the expected):
|
This seems "by design" - see here: And here: https://golang.org/pkg/net/http/#Response
I guess the HTTP usage pattern with this elastic client is slightly different - you're mostly not interested in response body (just the HTTP status code) when you're indexing documents. Not sure if you'd be keen to change the client so that it internally consumes the response body "eagerly", thus allowing the connection to be reused. This is quite problematic in high-throughput scenarios because it can easily cause the exhaustion of (ip, port) touples space when a new connection is created for each request. |
Right, that's "obvious" from the Go design, but perhaps not from the usual client usage. Thanks for all the info — I'll think about some good way to highlight this in the documentation. |
Hi. Just for note. Found this thread after two hours of debugging gorouting/mem leak. In my case I've just ignored response from bulk op: |
I've added a note about closing and consuming the reponse body in order to re-use connections into the README in bbd6b17 — thanks for raising the issue. @turboezh Curious about a use case where you send a bulk request, but you're not interested in the response, ie. you don't care if documents have been indexed succesfully or not? |
@karmi I'm new to ES and this API, wasn't sure how to properly use bulk and there is some kind of a prototype code here. I assumed that |
@turboezh , right, Have a look at |
@karmi Thank you! |
hi when i use context to control request timeout,i also find goroutinue leak。 0x498ba6 sync.runtime_SemacquireMutex+0x46 /usr/local/go/src/runtime/sema.go:710x4c539b sync.(*Mutex).lockSlow+0xfb /usr/local/go/src/sync/mutex.go:1380xf965b7 sync.(*Mutex).Lock+0xb47 /usr/local/go/src/sync/mutex.go:810xf965c0 github.com/elastic/go-elasticsearch/v7/estransport.(*Client).Perform+0xb50 /mnt/hgfs/go/pkg/mod/github.com/elastic/go-elasticsearch/v7@v7.6.0/estransport/estransport.go:2090xd500f0 github.com/elastic/go-elasticsearch/v7/esapi.MgetRequest.Do+0x920 /mnt/hgfs/go/pkg/mod/github.com/elastic/go-elasticsearch/v7@v7.6.0/esapi/api.mget.go:1680xeb8348 github.com/elastic/go-elasticsearch/v7/esapi.newMgetFunc.func1+0x198 /mnt/hgfs/go/pkg/mod/github.com/elastic/go-elasticsearch/v7@v7.6.0/esapi/api.mget.go:23 |
Greetings,
The documentation (and issue tracker, see #118) seems to imply that HTTP connections should be reused, but that doesn't seem to be the case. Here is a small app:
Before running the app
netstat -an | grep 9200
shows:If I then execute
netstat -an | grep 9200 | awk '{print $6}' | sort | uniq -c
while the app is running, the number of connections in TIME_WAIT state keeps growing. Running it immediately after the app finishes:Reproduced on both linux and MacOS with Elasticsearch 7.5.1, go 1.13.6 and elastic/go-elasticsearch 7.5.0.
Many thanks,
damjan
The text was updated successfully, but these errors were encountered: