0

I want my request to trigger some long running action, that should be executed in the background. I wrote the following implementation that should process my action in a background, but in fact my request executed synchronously:

    @AsyncTimeout(10 * 60 * 1000)
    @PostMapping("/dosomething")
    Mono<Void> doSomething() {
        return Mono.defer(() -> delay()).subscribeOn(Schedulers.elastic());
    }

    public static Mono<Void> delay() {
        return Mono.fromRunnable(() -> {
            LOG.info("Started sleep");
            Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
            LOG.info("Finished sleep");
        });
    }

in logs I see the following:

14:29:58.801 DEBUG 1 --- [p1491676195-768] Incoming request for POST http://localhost:8989/dosomething
14:29:58.802  INFO 1 --- [      elastic-6] Started sleep
14:30:08.803  INFO 1 --- [      elastic-6] Finished sleep
14:30:08.806 DEBUG 1 --- [p1491676195-655] Outgoing response for POST http://localhost:8989/dosomething: 200

I see that my sleep is executed in another thread, but for some reason my original request waits for sleep to finish

Update 1:

    @PostMapping("/dosomething")
    Mono<Void> doSomething() {
        return Mono.defer(() -> {
            delay().subscribe();
            return Mono.empty();
        });
a3dsfcv
  • 1,146
  • 2
  • 20
  • 35
  • Your outgoing response is written with a different thread (judging from the logging). The request its still handled async, and you will get a response when that finishes. The fact that you use reactive on the server doesn't suddenly change the behavior of HTTP with a request/response. – M. Deinum Apr 01 '21 at 14:50
  • @M.Deinum am, I missed the response is written by the different thread indeed! But how can I make it to print the response without waiting for "sleep" to finish? – a3dsfcv Apr 01 '21 at 15:03
  • Why would you want that? If you just want to start a background job then do that as a separate mono and don't return it. – M. Deinum Apr 01 '21 at 15:07
  • But if I will not return a mono - no one will subscribe to it and it will not be executed? Can you give an example, please – a3dsfcv Apr 01 '21 at 15:12
  • the reason why I need this is to start heavy calculations, that will be stored in the DB after the completion – a3dsfcv Apr 01 '21 at 15:18
  • Just subscribe to it yourself. And return an empty Mono. It will then run in the background without being tied to the other stream. – M. Deinum Apr 01 '21 at 15:25
  • But when you subscribe to mono you can not return Mono.Empty before the previous one completes. Please check my update. I understand why it does not work but can not understand how to make it work – a3dsfcv Apr 01 '21 at 16:35

1 Answers1

2

Based on the comments I've read, and from how I understood your concern, if you are expecting to run a blocking call but run it in the background while immediately returning back a response to the service call, you can simply add the subscribeOn to that process which in your example is the delay(). This will make it run in a different thread:

@PostMapping("/dosomething")
    Mono<Void> doSomething() {
        return Mono.defer(() -> {
            delay().subscribeOn(Schedulers.boundedElastic()).subscribe();
            return Mono.empty();
        });

This link may be related to your concern: https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking

nmina
  • 276
  • 3
  • 8
  • just wondering, is it gonna be another solution if method "delay" is a true reactive as well? I just dont want to wait until it finishes – a3dsfcv Apr 01 '21 at 21:34
  • In that case you most likely won't have to worry about it since Reactor(Webflux) will handle it properly for you. – nmina Apr 01 '21 at 21:58