How to throw an exception in a function from another thread?

baal_imago :

I've encountered a very odd problem: Basically, I'm implementing a server streaming grpc client in java and I'm using microprofiles as library/structure. Microprofiles has very handy interceptors when it comes to automatic retries and fallback, but it's dependent on exceptions being thrown in the intercept-annotated function, otherwise the retries aren't being triggered.

For unnary grpc calls, this works just fine, it works very similarly to an ordinary REST call. But when making a client to grpc server side stream, protobuf will create it's own thread and ask for a callback to handle onNext, onError and onCompleted. So when onError is called in the stream scope, any exception thrown won't be sent back to whatever function was used to start the stream, so there's no exceptions thrown to trigger the @Retry.

It's not possible to change how the asynchronous stream is handled and it's not possible to change how the @Retry is being triggered. Grpc is generated code, and the @Retry trigger is based on the microprofile library.

Example:

  @Retry(
          retryOn = {IOException.class, TimeoutException.class, StatusRuntimeException.class},
          maxDuration = 10,
          durationUnit =  ChronoUnit.SECONDS,
          maxRetries = 1,
          delay = 10,
          delayUnit = ChronoUnit.SECONDS
  )
  public void subscribeToLocations() {
    // --> Throwing an exception here triggers the @Retry <--
    SubscribeRequest locSubscribeRequest = SubscribeRequest.newBuilder().build();
    streamObserver = grpcStreamHandler();
    grpcBlockingstub.subscribeServerStreaming(locSubscribeRequest, streamObserver); // Can't change this.
  }

  private StreamObserver<SubscribeResponse> grpcStreamHandler() {
    return new StreamObserver<SubscribeResponse>() {
      @Override
      public void onNext(SubscribeResponse value) {
        // Handle grpc response
      }

      @Override
      public void onError(Throwable t) {
        // --> ERROR: Here, it should trigger the @Retry somehow. <--
      }

      @Override
      public void onCompleted() {
        // Handle oncomplete
      }
    };
  }

I've tried to find a solution to this for longer than I wish to admit, but still I'm at a loss. Is there a way to throw an exception in one scope which ends up in another? Is there some other solution?

udalmik :

You mix sync and async operations here, so you need to clearly define what is "success" for you and you don't need a retry raised. It can depend on your logic, few options with streaming:

  • At least one update to onNext received, then completion or error
  • onCompleted received with or without any updates
  • etc

Once you define the "success" rule, you can wrap subscription into Future and wait for it to be resolved with provided timeout. If not resolved, then throw Exception from original invocation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=372300&siteId=1