1

I have a method where I expect the following sequence of calls:

  1. sendUpdateCallback
  2. sendSuccessCallback

Here's the place I'm calling from:

  @Override
  public String onPaymentInitiationAuthorizationSuccess(
    @NotEmpty String userId,
    @NotEmpty String paymentExtra,
    @NotEmpty String paymentProduct
  ) {
    log.error("onPaymentInitiationAuthorizationSuccess tag");
    Map<String, String> paymentExtraMap = parseExtra(paymentExtra);

    String sessionSecret = paymentExtraMap.get(SDKConstants.KEY_SESSION_SECRET);
    String status = getStatusOfPaymentProduct(paymentProduct);
    SessionSuccessCallbackRequest params = new SessionSuccessCallbackRequest(userId, status);
    sessionsCallbackService.sendUpdateCallback(sessionSecret, params);
    if (!StringUtils.isEmpty(sessionSecret)) sessionsCallbackService.sendSuccessCallback(sessionSecret, params);

    return paymentExtraMap.getOrDefault(SDKConstants.KEY_RETURN_TO_URL, "");
  }

Here is the code of my methods:

@Service
public class SessionsCallbackService extends CallbackRestClient {
    private static final Logger log = LoggerFactory.getLogger(SessionsCallbackService.class);

    @Async
    public void sendUpdateCallback(String sessionSecret, BaseCallbackRequest params) {
        log.error("sendUpdateCallback");
        String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/update");
        sendSessionCallback(url, sessionSecret, params);
    }

    @Async
    public void sendSuccessCallback(String sessionSecret, BaseCallbackRequest params) {
        log.error("sendSuccessCallback");
        String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/success");
        sendSessionCallback(url, sessionSecret, params);
    }

But in logs i see next: enter image description here

As you can see from the screenshot, it is clear that I first call the sendUpdateCallback method and after sendSuccessCallback, but we see on requests that sendSuccessCallback is implemented faster, how can this be fixed?

Morozov
  • 4,968
  • 6
  • 39
  • 70
  • These are asynchronous services. You can't necessarily expect them to return in the same order you sent them. The first one takes longer than the second one so you get the second response back first. The way to fix it is to wait for the first response and then send the second request. – Dave S Nov 11 '21 at 22:27
  • @DaveS It is logical, but how can this problem be solved? I know that in `Kotlin`, for example, there is such a thing as coroutines that solve this problem, how to solve it in spring? – Morozov Nov 11 '21 at 22:29
  • 1
    If you want them to run sequentially, just remove the `@Async` annotation. You have 2 separate functions which run in the background. It is totally indeterministic which one would call a given endpoint first. This would be the same even if you were implementing it with Kotlin coroutins. – Ervin Szilagyi Nov 11 '21 at 22:33

1 Answers1

2

To fix the problem (make asynchronous (void) "service" (methods) synchronous), you could (if the effort is worth/not too big), refactor your void (semi-blocking) method to (Completable)Future<Void> like:

@Async
public CompletableFuture<Void> sendUpdateCallback(String sessionSecret, BaseCallbackRequest params) {
    log.error("sendUpdateCallback");
    String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/update");
    sendSessionCallback(url, sessionSecret, params);
    return CompletableFuture.completedFuture(null);
}

... then to "block" it, you would:

CompletableFuture<Void> updateResult = sessionsCallbackService.sendUpdateCallback(sessionSecret, params);
if (!StringUtils.isEmpty(sessionSecret)) {
  updateResult.get(); // also possible: get(long timeout, TimeUnit unit)
  if (updateResult.isCancelled()) {
     // ...will not happen in my answer, but for completeness..
  } else if (updateResult.isCompletedExceptionally()) {
    // handle/throw exception?
  } else if (updateResult.isDone()) { 
    // finally send success
    sessionsCallbackService.sendSuccessCallback(sessionSecret, params);
  }
}

But easiest (& convincing) is: Ervin's comment


xerx593
  • 12,237
  • 5
  • 33
  • 64