javaspring-bootspring-async

Executing and handling Void @Async operations from Spring Boot resources


Java 8 and Spring Boot 2.x here. I have a RESTful resource that will kick off a long-running operation, potentially taking 15 - 20 minutes to complete in some cases. I think I want to leverage the @Async annotation here in the service-layer, but I'm open to any good, elegant Spring Boot-savvy solution!

My best attempt thus far:

@RestController
@RequestMapping(path = "/v1/fizzbuzzes")
public class FizzbuzzResource {

    @Autowired
    private FizzbuzzService fizzbuzzService;

    @Autowired
    private FizzbuzzRepository fizzbuzzRepository;

    @Autowired
    @Qualifier("fizzbuzz.ids")
    private List<String> fizzbuzzList;

    @PostMapping("/{fizzbuzzId}")
    public ResponseEntity<Void> runFizzbuzzOperations(@PathVariable String fizzbuzzId)
            throws ExecutionException, InterruptedException {

        ResponseEntity responseEntity;

        // verify the fizzbuzzId is valid -- if it is, we process it
        Optional<Fizzbuzz> fbOpt = fizzbuzzRepository.lookupMorph(fizzbuzzId);
        if (fbOpt.isPresent()) {

            fizzbuzzList.add(fizzbuzzId);

            CompletableFuture<Void> future = fizzbuzzService.runAsync(fbOpt.get());
            future.get();
            // TODO: need help here

            // TODO: decrement the list once the async has completed -- ONLY do once async has finished
            fizzbuzzList.remove(fizzbuzzId);

            // return success immediately (dont wait for the async processing)
            responseEntity = ResponseEntity.ok().build();

        } else {
            responseEntity = ResponseEntity.notFound().build();
        }

        return responseEntity;

    }

}

@Service
public class FizzbuzzService {

    @Async
    public CompletableFuture<Void> runAsync(Fizzbuzz fizzbuzz) {

        // do something that can take 15 - 20 mins to complete
        // it actually is writing a massive amount of data to the file system
        // so there's nothing to really "return" so we just return null (?)

        return CompletableFuture.completedFuture(null);

    }

}

I think I'm close, but I'm struggling with:

Can anyone spot where I'm going awry?


Solution

    1. Remove call to CompletableFuture.get(). It waits for future to complete.
    2. Pass consumer to thenAccept. You can find examples here.
    3. For time outs, in Java 9, check orTimeOut. Check this for example. If you check the article in previous link, in Java 8, there is no clean way of handling timeouts. You can set default timeout for entire application by providing custom async executor. Check this stackoverflow question for solution.