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

speech: API is leaking connections #711

Closed
snktagarwal opened this issue Jul 29, 2017 · 17 comments
Closed

speech: API is leaking connections #711

snktagarwal opened this issue Jul 29, 2017 · 17 comments
Assignees
Labels
api: speech Issues related to the Speech-to-Text API. priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release.
Milestone

Comments

@snktagarwal
Copy link

I am using Speech API v0.10.0 and running into open connections in my client. I am using the following stub code:
main() {
....
477 ctx := context.Background()
478 // Create a google speech API client
479 client, err = speech.NewClient(ctx, option.WithServiceAccountFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")))
480 if err != nil {
481 log.Println("Err: Cannot create speech client")
482 return
483 }
484 defer client.Close()
}

116 ctx := context.Background()
117 speechStream, err := client.StreamingRecognize(ctx)

I call the following when I finish reading audio like this:

264 err := speechStream.CloseSend()
265 if err != nil {
266 log.Printf("CloseSend failed due to %+v", err)
267 }

I am consistently seeing that CloseSend succeeds (err is nil) but I keep having a increase in number of connections. I tried to GRPC debug this before coming here: grpc/grpc-go#1402

Reading from above issue it looks like creating a new RPC should not always mean creating a new connection. Although, the way I am leaking it looks like each streaming RPC is leaking a dangling client connection. Other notes:

  1. main() is called once and creates a RPC server
  2. I stream audio every 55s (because of 1min limit) and that's when I create a new speechStream. I close the speechStream after the 55s deadline.

Is this the right way to go around the 55 sec deadline when doing transcription? Is there any behavior of closing streams down that I am not handling properly?

@jba jba self-assigned this Jul 29, 2017
@jba jba added this to the Fixit 2017 Q3 milestone Jul 29, 2017
@jba jba added priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release. api: speech Issues related to the Speech-to-Text API. labels Jul 29, 2017
@snktagarwal
Copy link
Author

Hi, I see you updated the bug. is this in triage stage or an issue you know about?

@snktagarwal
Copy link
Author

snktagarwal commented Jul 31, 2017

`
import (
"io"

"fmt"                                                                                                               
_ "net"                                                                                                             
"net/http"                                                                                                          
_ "net/http/pprof"                                                                                                  
"testing"                                                                                                           
"cloud.google.com/go/speech/apiv1"                                                                                  
"golang.org/x/net/context"                                                                                          
speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1"                                                    

)

func TestExampleClient_StreamingRecognize(t *testing.T) {
for i := 0; i < 10; i++ {
ctx := context.Background()
c, err := speech.NewClient(ctx)
if err != nil {
// TODO: Handle error.
}
stream, err := c.StreamingRecognize(ctx)
if err != nil {
// TODO: Handle error.
}
go func() {
reqs := []*speechpb.StreamingRecognizeRequest{
// TODO: Create requests.
}
for _, req := range reqs {
if err := stream.Send(req); err != nil {
// TODO: Handle error.
}
}
stream.CloseSend()
}()
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
// TODO: handle error.
}
// TODO: Use resp.
_ = resp
}
}
fmt.Printf("Starting the server ...")
fmt.Println(http.ListenAndServe("localhost:6060", nil))
fmt.Printf("Ending the server ...")
}`

Use the above code to repro the issue. Go to localhost:6060/debug/pprof/ and if you will see 9 thread leaks like this:

10 @ 0x105aabd 0x106b8aa 0x106a4cc 0x1587e02 0x1089921
#	0x1587e01	google.golang.org/grpc/transport.(*http2Client).controller+0x9a1	/Users/sanket/go/src/google.golang.org/grpc/transport/http2_client.go:1227

10 @ 0x105aabd 0x106b8aa 0x106a4cc 0x15b228d 0x15cb747 0x1089921
#	0x15b228c	google.golang.org/grpc.(*addrConn).transportMonitor+0xc6c	/Users/sanket/go/src/google.golang.org/grpc/clientconn.go:1030
#	0x15cb746	google.golang.org/grpc.(*ClientConn).resetAddrConn.func1+0x266	/Users/sanket/go/src/google.golang.org/grpc/clientconn.go:759

@jba
Copy link
Contributor

jba commented Aug 1, 2017

Thanks for the repro. It's not a known issue. I will look at it shortly.

@rakyll rakyll changed the title Speech API leaking connections speech: API is leaking connections Aug 2, 2017
@pongad
Copy link
Contributor

pongad commented Aug 9, 2017

@snktagarwal I ran your code and observed 37 goroutines at the end. Is this what you're seeing? If I increase the number of iterations to 20, I end up with 67 goroutines.

However, the Client object can be reused and safely used by multiple goroutines. If I call NewClient only once before the loop, I only end up with 7 goroutines. If I call c.Close before ListenAndServe, I see only 3. From the stack, it looks like all of them are spawned by the HTTP server itself, so I don't think there's a leak there.

From your issue description, your server is creating only one client, is this right?

@snktagarwal
Copy link
Author

Ahan. Good catch. I never thought about reusing the "client" across goroutines. You're right, currently I create a client for each RPC call in my server.

I will use this approach to reduce the amount of leaks. But, I think its still a bug (maybe a lower priority now) that the Client has dangling RPC watchers (thread leaks that you saw when client was created multiple times).

@pongad
Copy link
Contributor

pongad commented Aug 10, 2017

@snktagarwal Potentially, yes. Though, I didn't see any leaks in my testing. As noted in my last comment, after closing the speech client, I only see 3 goroutines running, all of them seems to be spawned from ListenAndServe itself.

Since this is already tracked in grpc/grpc-go#1402 and the observed leak is from grpc not the client lib, I'll close this issue. Please reopen if you think it belongs here.

@pongad pongad closed this as completed Aug 10, 2017
@snktagarwal
Copy link
Author

(Not sure how to reopen the issue)

I think the purpose of the client is to be created once and used across. I have a (golang) server that is accepting audio from a NodeJS UI and pumping it into my server. I only want to create one NewClient() in main() and then use it throughout my RPCs. If I have to close the client for each RPC that defeats the purpose of the API and hence this is still a bad bug since it promotes bad coding practice.

On a second note, somehow even after doing CloseSend on the Client I am getting thread leaks in my server code. It may be the case that somewhere my server is holding onto objects and not letting them GC.

@pongad
Copy link
Contributor

pongad commented Aug 28, 2017

@snktagarwal I'm not sure what you're asking in the first paragraph. The client can be safely used concurrently by many goroutines. There shouldn't be anything stopping you from calling NewClient only once. Stream, on the other hand, are trickier, but those should be scoped to each request, right?

@snktagarwal
Copy link
Author

snktagarwal commented Aug 28, 2017

What I am claiming is if I do something like:

c = NewClient()
while processingRequests {
rec = c.RecognizeRequest()
.....
rec.CloseSend() // <--!! This is creating a thread leak which does not go away until c.Close() is called
}

// wait for a long time to serve RPCs
c.Close()

during the time that there are 100s of RecognizeResults() are called thread leaks which is not acceptable for a server. Does this makes more sense? You can use the above code to repro this issue.

func TestExampleClient_StreamingRecognize(t *testing.T) {
	ctx := context.Background()
	c, err := speech.NewClient(ctx)
	for i := 0; i < 10; i++ {
		if err != nil {
			// TODO: Handle error.
		}
		stream, err := c.StreamingRecognize(ctx)
		if err != nil {
			// TODO: Handle error.
		}
		go func() {
			reqs := []*speechpb.StreamingRecognizeRequest{
			// TODO: Create requests.
			}
			for _, req := range reqs {
				if err := stream.Send(req); err != nil {
					// TODO: Handle error.
				}
			}
			stream.CloseSend()
		}()
		for {
			resp, err := stream.Recv()
			if err == io.EOF {
				break
			}
			if err != nil {
				// TODO: handle error.
			}
			// TODO: Use resp.
			_ = resp
		}
	}
	fmt.Printf("Starting the server ...")
	fmt.Println(http.ListenAndServe("localhost:6060", nil))
	fmt.Printf("Ending the server ...")
	c.Close() // call close after we have served all RPCs
}

@jba jba reopened this Aug 29, 2017
@pongad
Copy link
Contributor

pongad commented Aug 29, 2017

I edited your code snippet for spacing. I'll take a look at this again.

@pongad
Copy link
Contributor

pongad commented Aug 30, 2017

@snktagarwal I ran the code above (importing "net/http/pprof"). After printing "starting the server", I went to http://localhost:6060/debug/pprof/goroutine?debug=1.

The dump shows only 7 goroutines. Both google.golang.org/grpc/transport.(*http2Client).controller and google.golang.org/grpc.(*addrConn).transportMonitor have 1 goroutine each.

Could it be you are using an outdated grpc package?

@snktagarwal
Copy link
Author

snktagarwal commented Aug 30, 2017 via email

@pongad
Copy link
Contributor

pongad commented Aug 31, 2017

@snktagarwal I used grpc from master earlier, but just tested with v1.5.2 and got the same results.

@pongad
Copy link
Contributor

pongad commented Oct 24, 2017

Since this issue has been inactive for a while, I'll close this. Please reopen if required.

@pongad pongad closed this as completed Oct 24, 2017
@Bankq
Copy link
Contributor

Bankq commented Nov 29, 2017

I'm seeing same issue with pubsub. Goroutine counts and mem usage keeps creeping up when I receive on a topic.

In my case there is only 1 goroutine calling receive.

Ok I found the issue: you have to explicitly call pubsub.Client.Close() to release all the connections. @snktagarwal this might apply to your case as well?

@jba do you think it's worth mentioning it explicitly in the doc? It caught me cuz doc says one don't have to call it at program exits but my service deal with pubsub topics from different projects thus I have to create pubsub clients on the fly. Not calling Close() leaks goroutines there.

@jba
Copy link
Contributor

jba commented Dec 1, 2017

I tried to clarify the doc in https://code-review.googlesource.com/#/c/gocloud/+/20250.

@Bankq
Copy link
Contributor

Bankq commented Dec 1, 2017

@jba tyvm! This is the moment I miss destructors :)

gopherbot pushed a commit that referenced this issue Dec 4, 2017
Clarify that Close releases goroutines and memory, and try
to explain better when you don't have to call it.

Updates #711.

Change-Id: I7f2fca57da4f5bbcd0d3d5a327d365457973c113
Reviewed-on: https://code-review.googlesource.com/20250
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Michael Darakananda <pongad@google.com>
yoshi-automation added a commit that referenced this issue Apr 13, 2022
This is an auto-generated regeneration of the gapic clients by
cloud.google.com/go/internal/gapicgen. Once the corresponding genproto PR is
submitted, genbot will update this PR with a newer dependency to the newer
version of genproto and assign reviewers to this PR.

If you have been assigned to review this PR, please:

- Ensure that the version of genproto in go.mod has been updated.
- Ensure that CI is passing. If it's failing, it requires your manual attention.
- Approve and submit this PR if you believe it's ready to ship.

Corresponding genproto PR: googleapis/go-genproto#793

Changes:

fix(compute): remove proto3_optional from parent_id (#712)

  Source-Link: googleapis/googleapis@fd16b6a

docs(firestore): clarifications for filters
  PiperOrigin-RevId: 441242400
  Source-Link: googleapis/googleapis@9ef0015

fix(compute): replace missing REQUIRED for parent_id (#711)

  Source-Link: googleapis/googleapis@4bb6fd6

docs(bigquery/reservation): cleanup and clarifications feat: add UpdateAssignment method feat: add "concurrency" and "multi_region_auxiliary" in reservation feat: add preferred table.
  PiperOrigin-RevId: 440912466
  Source-Link: googleapis/googleapis@7ab53ca

fix(speech): use full link in comment to fix JSDoc broken link
  Committer: @summer-ji-eng
  PiperOrigin-RevId: 440481666
  Source-Link: googleapis/googleapis@6a21110

chore(servicedirectory): remove unused imports
  PiperOrigin-RevId: 440391736
  Source-Link: googleapis/googleapis@5d84222

docs(datacatalog): update taxonomy display_name comment feat: added Dataplex specific fields
  PiperOrigin-RevId: 440386238
  Source-Link: googleapis/googleapis@99fd8be

chore(pubsublite): remove unused imports
  PiperOrigin-RevId: 440384445
  Source-Link: googleapis/googleapis@97350b3

feat(securitycenter): Add next_steps field to finding's list of attributes
  PiperOrigin-RevId: 440383959
  Source-Link: googleapis/googleapis@6a276f6

fix(dataproc)!: Move `yarn_config` into a `oneof`
  Committer: @Harwayne
  PiperOrigin-RevId: 440226213
  Source-Link: googleapis/googleapis@c782e45

fix(dataproc)!: Remove `temp_bucket` from VirtualClusterConfig, as its value was not used
  Committer: @Harwayne
  PiperOrigin-RevId: 440224385
  Source-Link: googleapis/googleapis@afc5066

feat(monitoring/dashboard): Sync public protos with latests public api state. This adds support for collapsible groups, filters, labels, drilldowns, logs panels and tables
  PiperOrigin-RevId: 440139643
  Source-Link: googleapis/googleapis@2bff0f3
yoshi-automation added a commit that referenced this issue Apr 13, 2022
This is an auto-generated regeneration of the gapic clients by
cloud.google.com/go/internal/gapicgen. Once the corresponding genproto PR is
submitted, genbot will update this PR with a newer dependency to the newer
version of genproto and assign reviewers to this PR.

If you have been assigned to review this PR, please:

- Ensure that the version of genproto in go.mod has been updated.
- Ensure that CI is passing. If it's failing, it requires your manual attention.
- Approve and submit this PR if you believe it's ready to ship.

Corresponding genproto PR: googleapis/go-genproto#793

Changes:

fix(compute): remove proto3_optional from parent_id (#712)

  Source-Link: googleapis/googleapis@fd16b6a

docs(firestore): clarifications for filters
  PiperOrigin-RevId: 441242400
  Source-Link: googleapis/googleapis@9ef0015

fix(compute): replace missing REQUIRED for parent_id (#711)

  Source-Link: googleapis/googleapis@4bb6fd6

docs(bigquery/reservation): cleanup and clarifications feat: add UpdateAssignment method feat: add "concurrency" and "multi_region_auxiliary" in reservation feat: add preferred table.
  PiperOrigin-RevId: 440912466
  Source-Link: googleapis/googleapis@7ab53ca

fix(speech): use full link in comment to fix JSDoc broken link
  Committer: @summer-ji-eng
  PiperOrigin-RevId: 440481666
  Source-Link: googleapis/googleapis@6a21110

chore(servicedirectory): remove unused imports
  PiperOrigin-RevId: 440391736
  Source-Link: googleapis/googleapis@5d84222

docs(datacatalog): update taxonomy display_name comment feat: added Dataplex specific fields
  PiperOrigin-RevId: 440386238
  Source-Link: googleapis/googleapis@99fd8be

chore(pubsublite): remove unused imports
  PiperOrigin-RevId: 440384445
  Source-Link: googleapis/googleapis@97350b3

feat(securitycenter): Add next_steps field to finding's list of attributes
  PiperOrigin-RevId: 440383959
  Source-Link: googleapis/googleapis@6a276f6

fix(dataproc)!: Move `yarn_config` into a `oneof`
  Committer: @Harwayne
  PiperOrigin-RevId: 440226213
  Source-Link: googleapis/googleapis@c782e45

fix(dataproc)!: Remove `temp_bucket` from VirtualClusterConfig, as its value was not used
  Committer: @Harwayne
  PiperOrigin-RevId: 440224385
  Source-Link: googleapis/googleapis@afc5066

feat(monitoring/dashboard): Sync public protos with latests public api state. This adds support for collapsible groups, filters, labels, drilldowns, logs panels and tables
  PiperOrigin-RevId: 440139643
  Source-Link: googleapis/googleapis@2bff0f3
codyoss pushed a commit that referenced this issue Apr 14, 2022
This is an auto-generated regeneration of the gapic clients by
cloud.google.com/go/internal/gapicgen. Once the corresponding genproto PR is
submitted, genbot will update this PR with a newer dependency to the newer
version of genproto and assign reviewers to this PR.

If you have been assigned to review this PR, please:

- Ensure that the version of genproto in go.mod has been updated.
- Ensure that CI is passing. If it's failing, it requires your manual attention.
- Approve and submit this PR if you believe it's ready to ship.

Corresponding genproto PR: googleapis/go-genproto#793

Changes:

fix(compute): remove proto3_optional from parent_id (#712)

  Source-Link: googleapis/googleapis@fd16b6a

docs(firestore): clarifications for filters
  PiperOrigin-RevId: 441242400
  Source-Link: googleapis/googleapis@9ef0015

fix(compute): replace missing REQUIRED for parent_id (#711)

  Source-Link: googleapis/googleapis@4bb6fd6

docs(bigquery/reservation): cleanup and clarifications feat: add UpdateAssignment method feat: add "concurrency" and "multi_region_auxiliary" in reservation feat: add preferred table.
  PiperOrigin-RevId: 440912466
  Source-Link: googleapis/googleapis@7ab53ca

fix(speech): use full link in comment to fix JSDoc broken link
  Committer: @summer-ji-eng
  PiperOrigin-RevId: 440481666
  Source-Link: googleapis/googleapis@6a21110

chore(servicedirectory): remove unused imports
  PiperOrigin-RevId: 440391736
  Source-Link: googleapis/googleapis@5d84222

docs(datacatalog): update taxonomy display_name comment feat: added Dataplex specific fields
  PiperOrigin-RevId: 440386238
  Source-Link: googleapis/googleapis@99fd8be

chore(pubsublite): remove unused imports
  PiperOrigin-RevId: 440384445
  Source-Link: googleapis/googleapis@97350b3

feat(securitycenter): Add next_steps field to finding's list of attributes
  PiperOrigin-RevId: 440383959
  Source-Link: googleapis/googleapis@6a276f6

feat(monitoring/dashboard): Sync public protos with latests public api state. This adds support for collapsible groups, filters, labels, drilldowns, logs panels and tables
  PiperOrigin-RevId: 440139643
  Source-Link: googleapis/googleapis@2bff0f3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: speech Issues related to the Speech-to-Text API. priority: p1 Important issue which blocks shipping the next release. Will be fixed prior to next release.
Projects
None yet
Development

No branches or pull requests

4 participants