gRPC: How to close a stream on the client

Scenario: App is a gRPC client that needs to initiate two different streaming connections to the same server.

If one of them goes bad - returns an error response - both need to be reset.

Let's say Stream 2 goes bad. The problem: If, on receipt of the next message in the Stream 1 loop (calling Streaming.message()) I break out of the loop and re-init the stream (calling the method on the service that returns Streaming<_>), the server returns the error, "Active streaming subscription already exists." It refuses to create another connection.

This occurs even if I call drop on the Streaming instance.

Thus I am at a loss of how to handle this. I can't kill the thread when it's blocking on message() (because there's no way to kill threads). I can't re-init the stream because drop apparently doesn't close it. What to do?

Without knowing anything about gRPC, just scanning the docs:

  • In general it may take some time for sockets to timeout, so you may have to keep retrying periodically for a while.
  • You may need to go all the way back to the connect call, if you're not doing that already.

Just guessing.

Also: If you haven't already, check the server logs to see if there are errors during shutdown, etc.

There's no access to the server log - this is a SaaS product.

I should've mentioned there's a 10s sleep in the thread already. Making it longer is an option - but boy is this hacky.

I've used gRPC in other languages (mainly JVM), and there was a way to close a stream from the client. This would result in an immediate server interrupt. I am pretty sure this is simply a weakness in tonic as there seems to be no way to do it.

I think you're right. Is this the same issue you're seeing?:

In the past we've implemented heartbeats to work around this problem (for sockets in general), and I see a heartbeat issue referenced from the above issue:

No, not really. I mean there is no way to intentionally close a server-side streaming connection from the client.

However, waiting long enough makes it work.