Best way to start a long running task from a REST Endpoint in Quarkus?

Viewed 758

I want to start a long running task (let's say it will take 15m to complete) from a HTTP REST endpoint in Quarkus.

It must not block the event loop thread and my task should not be cancelled by any timeouts. Obviously I cannot directly do the work in the REST endpoint. I tried to use CDI events but they turned out to be executed synchronously which will result in the same issue.

I tried Vert.x event bus but I noticed that I just moved the exactly same problem from the REST endpoint to the event handler which is also by default executed on the event loop thread.

2 Answers

I now used a managed executor. However, now I don't have any control over the running task and it could run for days. It seems that I'll have to implement my own monitoring.

  @Inject
  ManagedExecutor executor;

  @POST
  public void startWork() {
    executor.submit(() -> executeLongRunningTask());
  }

You could include

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-mutiny</artifactId>
    </dependency>

in your project. This will give you the ability to use Unis, and thus achieve asynchronous calls to your service.

You can then set a time limit on the call to a duration of 15 or 20 minutes and then fail the call.

Something like this

Uni<String> uniWithTimeout = uni.ifNoItem().after(Duration.ofMinutes(20)).fail();

NOTE: Duration comes from java.time

The example I posted above came from Mutiny's own documentation.

This should provide some ease in handling your long running code as well as giving you the desired control over runaway processes. Be sure to add some logging messages as well to aid you in why the process was killed.

Good Luck!

EDIT

So to Answer the question posed in the comment, let me start off by confessing, I myself am just getting started with reactive/asynchronous programming using mutiny. But here is how I would approach this situation.

Let's assume you have the following endpoint in your RESTful service

@GET
@Path("longtask")
public Response longRunningTask(){
    return new Response.status(200).entity(longrunningtask()).build()
}

Granted, this may not be the most elegant approach, but for demonstration sake let's just go with it.

I would change this in the following manner to utilize mutiny

@GET
@Path("longtask")
public Uni<Respone> longRunningTask(){
    Uni<Response> resp = Uni.createFrom()
                            .item(longrunningtask())
                            .ifNoItem()
                            .after(Duration.ofMinutes(20))
                            .fail();
}

DISCLAIMER: The above is merely a demonstration and not meant necessarily be working code. Though the structure is close.

Again, this may not be the most elegant way to approach it, but this is how I would go about trying to tackle such a situation.

Related