Skip to content
Advertisement

stop other service calls if using Completable future to call multiple services if any of service fails

I am using completable future to invoke multiple service calls.

CompletableFuture<Response1> future1 = CompletableFuture.supplyAsync(() -> webServiceCall1());
CompletableFuture<Response2> future2 = CompletableFuture.supplyAsync(() -> webServiceCall2());
CompletableFuture<Response3> future3 = CompletableFuture.supplyAsync(() -> webServiceCall3());

And then I am using :

Response1 response1 = future1.get();
Response2 response2 = future2.get();
Response3 response3 = future3.get();
// do further processing on the response

Well why am i using this kind of code ??? by doing that I am able to improve performance. If service1 succeeds with 200 & takes 3 sec of time , service2 succeeds with 200 & takes 3 sec of time , service3 succeeds with 200 & takes 3 sec of time , so calling them one after other will take overall 9 secs of time. But if I use what I have written above, all of them will be executing parallel & will finish around in 3 secs of time.

now issue is that if webservice1 fails, then hit to webservice2 & webservice3 will not be needed. but in my scenario, since the code is written in such a way that it calls webservice2 & webservice3, even if webservice1 fails.

How we can avoid this & still keep webservice calls parallel & still fail safe at the same time ??

Let me rephrase myself — How we can keep the overall performance of this code high (considering benchmark of somewhere around 3 secs) & still fail safe at the same time ??

Advertisement

Answer

  • webServiceCall1() will fire off no matter what
  • webServiceCall2() will check webServiceCall1()
    • Check to see if a response status has been returned yet for 1
      • If the response status is 200, go ahead and fire
      • If the response status is not 200, do not make a call
      • If the response status has not been returned yet, go ahead and make the call anyways – essentially, we are assuming it will succeed
  • webServiceCall3 will check both webServiceCall1() and webServiceCall2()

I want to highlight though – if webServiceCall1() has not completed by the time we attempt to do webServiceCall2(), then we are assuming that webServiceCall1() has returned and is successful with a 200 response code. If it ends up that webServiceCall1() is not successful even after assuming it was, then we will have made a pointless call to webServiceCall2() (and potentially webServiceCall3()), so be aware of that.

My solution ONLY covers the edge case of webServiceCall1() firing off and failing so quickly that we get our response back before we have a chance to check it in the 2nd CompletableFuture. Same for webServiceCall2() and the 3rd CompletableFuture. In both of these situations, we will have optimized out 1 or 2 calls from occurring. But don’t assume that that will happen EVERY time webServiceCall1() or webServiceCall2() fails.

CompletableFuture<Response1> future1 = CompletableFuture.supplyAsync(() -> webServiceCall1());

CompletableFuture<Response2> future2 = CompletableFuture.supplyAsync(() -> (performServiceCheck(future1) ? webServiceCall2() : null));

CompletableFuture<Response3> future3 = CompletableFuture.supplyAsync(() -> (performServiceCheck(future1, future2) ? webServiceCall3() : null));
private boolean performServiceCheck(CompletableFuture<?> ...futures) {
    
    boolean result = true;
    
    for (CompletableFuture<?> future : futures) {
        if (future.isDone() && is200(future.get())) {
            result = result && true;
        } else if (future.isDone() && !is200(future.get())) {
            result = result && false;
        } else if (!future.isDone()) {
            result = result && true;
        }
    }
    
    return result;
    
}
Advertisement