Strategy of handling exceptions in C# client

1,725 views
Skip to first unread message

kkm.po...@gmail.com

unread,
Feb 19, 2016, 9:23:32 PM2/19/16
to grpc.io
Here is the problem I do not know how to handle correctly. I have a bidirectional stream with the server. Think of the line in the RouteGuideExample

    foreach (RouteNote request in requests)
    {
        await call.RequestStream.WriteAsync(request);  // <-- THIS
    }


 At some point the server denies a message and responds with an error status. The problem I have is extracting server's status message.

Normally (and I cannot figure out when exactly, but more often than not) I am thrown a nice RpcException containing the status, but not at this point. Instead, the stream throws an InvalidOperationException with the message "Already finished." Ok, I am catching that and can only hope this happened because the call had actually ended and returned a status. But what if not? Then a call to call.GetStatus() itself will throw an InvalidOperationException! In this case, I'd better retain the original InvalidOperationException, which should be more informative. All in all, I am wrapping each WriteAsync call in this monstrosity:

      try {
        await call.RequestStream.WriteAsync(req);
      } catch (InvalidOperationException exInvalOrig) {
        try {
          throw new RpcException(call.GetStatus());
        } catch (InvalidOperationException) {
          throw exInvalOrig;
        }
      }


I am already losing backtrace in exInvalOrig (there is a way around that in .NET 4.5, but I do not make this code even uglier). Is there really a better way? Is this actually a bug in grpc?

 -kkm

Jan Tattermusch

unread,
Feb 29, 2016, 7:21:26 PM2/29/16
to kkm.po...@gmail.com, grpc.io
Hi, 

the expected usage pattern is that you actually try to retrieve the server status from the   ResponseStream.MoveNext() instead. That makes much more sense, because you're probably expecting some responses from server.  Once the last reponse is received, MoveNext will either return false  or throw RpcException with appropriate status from the server.

Normally, sending a request should not fail unless there is a network problem (or the stream has already been closed by either the client or the server - but you are in control over when this happens) and you should rely on your read operations to deliver the resulting status from the server.

Jan




--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+unsubscribe@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/11591e17-d487-40ce-9a05-9ac18567b060%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

kkm.po...@gmail.com

unread,
Mar 18, 2016, 3:58:15 PM3/18/16
to grpc.io
Hi Jan,

Thanks for the reply. It took me a while to get back to this problem.

I admit I do not understand your suggestion. Maybe I should explain the problem in a bit more detail

My client establishes a call to the server, and sends multiple data messages. The server may like the message and continue receiving them, or it may decide that it does not like the data and the call should not proceed. My understanding was that the grpc::Status object that I return from the call is a mechanism to report errors; I just return from the call with my custom status message in this case.

If I understand your suggestion, I should just ignore InvalidOperationException in this case, break the loop and somehow synchronize with the asynchronous task that is receiving the messages from the server, because that task is the one that will throw an RpcException. Is my understanding correct?

If it is, is it a more or less strong guarantee?  Can InvalidOperationException be thrown for any other reason than call having ended? I just think that I can end up in a deadlock waiting for the other end closing forever, if I just blindly go into waiting for it to terminate with the meaningful RpcException, but the server will have not closed the call and would wait for more data instead. (Client cannot terminate the call, as Status will be lost). Handling call termination gracefully seems a dauntingly complex task in a C# client, unlike C++. Your help is really appreciated.

 -kkm


On Monday, February 29, 2016 at 4:21:26 PM UTC-8, Jan Tattermusch wrote:
Hi, 

the expected usage pattern is that you actually try to retrieve the server status from the   ResponseStream.MoveNext() instead. That makes much more sense, because you're probably expecting some responses from server.  Once the last reponse is received, MoveNext will either return false  or throw RpcException with appropriate status from the server.

Normally, sending a request should not fail unless there is a network problem (or the stream has already been closed by either the client or the server - but you are in control over when this happens) and you should rely on your read operations to deliver the resulting status from the server.

Jan



On Fri, Feb 19, 2016 at 6:23 PM, <kkm.po...@gmail.com> wrote:
Here is the problem I do not know how to handle correctly. I have a bidirectional stream with the server. Think of the line in the RouteGuideExample

    foreach (RouteNote request in requests)
    {
        await call.RequestStream.WriteAsync(request);  // <-- THIS
    }


 At some point the server denies a message and responds with an error status. The problem I have is extracting server's status message.

Normally (and I cannot figure out when exactly, but more often than not) I am thrown a nice RpcException containing the status, but not at this point. Instead, the stream throws an InvalidOperationException with the message "Already finished." Ok, I am catching that and can only hope this happened because the call had actually ended and returned a status. But what if not? Then a call to call.GetStatus() itself will throw an InvalidOperationException! In this case, I'd better retain the original InvalidOperationException, which should be more informative. All in all, I am wrapping each WriteAsync call in this monstrosity:

      try {
        await call.RequestStream.WriteAsync(req);
      } catch (InvalidOperationException exInvalOrig) {
        try {
          throw new RpcException(call.GetStatus());
        } catch (InvalidOperationException) {
          throw exInvalOrig;
        }
      }


I am already losing backtrace in exInvalOrig (there is a way around that in .NET 4.5, but I do not make this code even uglier). Is there really a better way? Is this actually a bug in grpc?

 -kkm

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.

kkm.po...@gmail.com

unread,
Mar 18, 2016, 4:21:07 PM3/18/16
to grpc.io, kkm.po...@gmail.com
On Monday, February 29, 2016 at 4:21:26 PM UTC-8, Jan Tattermusch wrote:
you should rely on your read operations to deliver the resulting status from the server.

If I could comment on this, I do indeed respect your design decisions, even though I could not understand them, but if WriteAsync, upon discovering that the call has completed, would throw an RpcException instead of InvalidOperationException , that would, from my not really very informed point of view, handling call status awesomely simpler. Please consider this a feature request.

 -kkm

Jan Tattermusch

unread,
Mar 26, 2016, 11:19:48 PM3/26/16
to kkm.po...@gmail.com, grpc.io
To treat it as a feature request, would you mind creating a github issue from this discussion? That way we can track it better.

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+unsubscribe@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.

kkm.po...@gmail.com

unread,
Mar 28, 2016, 5:02:22 PM3/28/16
to grpc.io, kkm.po...@gmail.com
Hi Jan, thank you, I did just that  (https://github.com/grpc/grpc/issues/5975), and wrote a new exposition of the issue there. Hope the new summary is clearer.

 -kkm


On Saturday, March 26, 2016 at 8:19:48 PM UTC-7, Jan Tattermusch wrote:
To treat it as a feature request, would you mind creating a github issue from this discussion? That way we can track it better.
On Fri, Mar 18, 2016 at 1:21 PM, <kkm.po...@gmail.com> wrote:
On Monday, February 29, 2016 at 4:21:26 PM UTC-8, Jan Tattermusch wrote:
you should rely on your read operations to deliver the resulting status from the server.

If I could comment on this, I do indeed respect your design decisions, even though I could not understand them, but if WriteAsync, upon discovering that the call has completed, would throw an RpcException instead of InvalidOperationException , that would, from my not really very informed point of view, handling call status awesomely simpler. Please consider this a feature request.

 -kkm

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.

To post to this group, send email to grp...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages