Using async grpc streaming APIs

599 views
Skip to first unread message

Piyush Sharma

unread,
Jun 4, 2021, 11:42:02 PM6/4/21
to grpc.io
(Working with grpc async bidirectional streaming)

  1. For ClientAsyncStreamingInterface::Finish and for ServerAsyncReaderWriterInterface::Finish the documentation says:

    "Should not be used concurrently with other operations"

    Does that mean there shouldn't be any outstanding Writes / Reads when this function is called? Or just that there shouldn't be a call to "Finish" when a call to "Read" or "Write" is in progress (as in multiple threads competing with each other)? I didn't see a problem with an outstanding read when I tested (by issuing a read, closing server side, closing client side in parallel) this, but just wanna confirm what’s ideal.

  2. For ClientAsyncStreamingInterface::Finish the doc says:

    "
    /// It is appropriate to call this method exactly once when both:
    /// * the client side has no more message to send
    /// (this can be declared implicitly by calling this method, or
    /// explicitly through an earlier call to the <i>WritesDone</i> method
    /// of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or
    /// \a ClientAsyncReaderWriterInterface::WritesDone).
    "

    Does this mean that a call to "WritesDone" before calling "Finish" is optional and I can just directly call "Finish" instead? If yes, in what circumstances would one find calling "WritesDone" before "Finish" useful?

  3. The documentation for CompletionQueue::Next says that for a client-side Read, a call to Next returns not-ok only when the "call is dead". What all does that exactly mean? What constitutes a "call"? I thought it could be not-ok if the server-side or client-side is already "Finish"-ed or if the underlying network was compromised.
     

  4. Also, is it ok to invoke ClientAsyncStreamingInterface::Finish before the server has sent all its messages and before a AsyncReaderInterface::Read yields a not-ok tag? I see that the second point in documentation for this "Finish" instructs exactly against such a usage, but I just wanted to confirm if it's an illegal usage and could result in run time errors (assert fails) from grpc code?
    Test: for not_ok_grpc_status “Finish” completes, but as even the doc states the “Finish” tag never comes out (as all the incoming messages have not been read by the client) when server responds with grpc_ok which may lead to minor resource_leaks? Can such usage lead to crashes?

  5. Also, what's the accepted way to close the streams when it's not implicitly known that no more messages are to be received from the server? Would it be:
    1. Invoke 'WritesDone'.
    2. Wait for AsyncReaderInterface::Read to yield a not-ok tag.
    3. Finish the stream.

yas...@google.com

unread,
Jun 16, 2021, 2:10:59 PM6/16/21
to grpc.io
> Does that mean there shouldn't be any outstanding Writes / Reads when this function is called? Or just that there shouldn't be a call to "Finish" when a call to "Read" or "Write" is in progress (as in multiple threads competing with each other)? I didn't see a problem with an outstanding read when I tested (by issuing a read, closing server side, closing client side in parallel) this, but just wanna confirm what’s ideal.

This has to do with API usage. It is not safe to call `Finish()` when a `Read()`/`Write()` operation is in progress (for example, if it is an asynchronous operation and the tag has not yet been received from the completion queue.)

> Does this mean that a call to "WritesDone" before calling "Finish" is optional and I can just directly call "Finish" instead? If yes, in what circumstances would one find calling "WritesDone" before "Finish" useful?

A `WritesDone()` would result in a half-close from the client and I can imagine cases where a half-close from the client is useful for the server to know (cases where there are still more messages to be read).

> The documentation for CompletionQueue::Next says that for a client-side Read, a call to Next returns not-ok only when the "call is dead". What all does that exactly mean? What constitutes a "call"? I thought it could be not-ok if the server-side or client-side is already "Finish"-ed or if the underlying network was compromised.

A "call" here is an RPC or a stream from a HTTP2 perspective. When `Read()` fails on the client, it is a good signal that the RPC has ended one way or another, either through a proper status received from the server or some error (includes network errors). This status/error would be received when `Finish()` is called.

> Also, is it ok to invoke ClientAsyncStreamingInterface::Finish before the server has sent all its messages and before a AsyncReaderInterface::Read yields a not-ok tag? I see that the second point in documentation for this "Finish" instructs exactly against such a usage, but I just wanted to confirm if it's an illegal usage and could result in run time errors (assert fails) from grpc code?

It is illegal usage of the API. I'm not sure if we have asserts in place for this though.

> Also, what's the accepted way to close the streams when it's not implicitly known that no more messages are to be received from the server? 

1. Invoking `WritesDone` is just a half-close and does not mean that the RPC is done. 
2. Waiting for `Read()` to fail is a sure-shot way of knowing that the RPC is done. 
3. It's not safe to invoke `Finish()` if we don't know that we are done reading. I believe the call would just get stuck here but I might be wrong as to the observable behavior here.
If we just want to end the RPC without caring about the server's status, then the client can also cancel the RPC with a `TryCancel()` on the ClientContext.
Reply all
Reply to author
Forward
0 new messages