Uploaded image for project: 'mdsal'
  1. mdsal
  2. MDSAL-20

Improve RPC API error handling


    • Icon: Improvement Improvement
    • Resolution: Unresolved
    • 14.0.0
    • None
    • Operating System: Linux
      Platform: PC

      At the moment, it’s pretty painful to try to attempt to deal with errors when implementing and using RPC APIs. Much of the reason for this is the use of the RpcResult object which contains a list of very rich RpcError objects that are returned in the case of an error. While in principle it’s nice to have such a rich notion of error handling, in practice it’s cumbersome to construct these objects and not always clear to what purpose all the various fields are to be put.
      Further, there is a mismatch between normal Java error-handling semantics and the way we need to interact with these calls.
      What follows is an attempt to actually handle errors in an RPC call. Note that to do this we need to write code that handles both Java exceptions as well as parsing the results of the RpcResult object. This code is hard to write, hard to read, and even worse, this code doesn’t even handle proper asynchronous semantics, so it’s not even correct.


      public Future<RpcResult<Void>> exampleRpc(ExampleRpcInput input) {
        // Other implementation here
        // ...
        List<RpcError> errors;
        try {
          RpcResult<TransactionStatus> commitresult = t.commit().get();
          errors = commitresult.getErrors();
        } catch (InterruptedException | ExecutionException e) {
          errors = List.of(RpcErrors.getRpcError(APPLICATION_TAG, objecttag, "commit error", ErrorSeverity.ERROR, "Could not " + action + " " + objecttag, ErrorType.RPC, e));
        return Future.immediateFuture(... RpcResultBuilder-based magic ...)

      We can do much better than this. First, Java already has a perfectly acceptable mechanism for handling errors: exceptions. Rather than returning an RpcResult with successful set to false and a list of associated errors, our method should throw an exception. This exception needs to be marshaled to the calling service, and thrown on a call to get() as an ExecutionException. We can subclass ExecutionException and add additional context information where appropriate.
      With these changes, our code above could become:

      ExampleService.java (Simplified error handling)

      public Future<RpcResult<Void>> exampleRpc(ExampleRpcInput input)  throws Exception {
         // Other implementation here
         // ...
         RpcResult<TransactionStatus> commitresult = t.commit().get();
         RpcResult<Void> result = Rpcs.<Void>getRpcResult();
         return Futures.immediateFuture(result);

      We could simplify this further to get rid of RpcResult as well now.

            Unassigned Unassigned
            readams Rob Adams
            0 Vote for this issue
            4 Start watching this issue