naddame
naddame

Reputation: 139

Quarkus : How to setup a global Custom ThreadPoolExecutor

I am working on project where context and session are managed by ThreadLocal safely with ThreadPoolExecutor ( information is passed from Thread to another inside ThreadPoolExecutor).

We have:

  1. ThreadPoolExecutor: It implements beforeExecute and afterExecute methods behaviour to ensure that informations is passed from Thread to another and Thread context is cleaned in the afterExecutre method.
  2. ThreadFactory: Responsible of creating a custom named Thread that hold the ThreadLocal session information.
  3. A custom Runnable used in beforeExecute. This runnable use original one + other needed information (session for example)

It works fine and tested in high level production project.

Quarkus context propagation works fine. But migrating the current (production) project to quarkus seem to be hard and risky.

So, how, for example, @Provide a CustomThreadPool, and quarkus can use it as principal ThreadPool instead of default one.

Upvotes: 0

Views: 4461

Answers (1)

Pablo
Pablo

Reputation: 21

Quarkus version: 1.9.1.Final

Java: 11

Well, you could tune Quarkus main service executor by configuration properties. Have a look all quarkus.thread-pool.* properties and then use it as a regular service executor.

In order to inject this thread pool into your service, be sure that you support context propagation by adding the following extension ./mvnw quarkus:add-extension -Dextensions="quarkus-smallrye-context-propagation" as you can see in this link

Inject the main service executor into your service is as simple as:

@Inject ManagedExecutor exec;

Create a separate service executor it's another approach (however you should ask yourself, What I get with a separate executor?, What I am looking for?), maybe make sense, I guess that depends on the case. In 'this case', you could create your own ManagedExcutor and use it, for example:


import io.quarkus.runtime.Startup;
import org.eclipse.microprofile.context.ManagedExecutor;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import javax.ws.rs.Produces;

public class Producers {

    private final static int POOL_SIZE = 4; // TODO move on this hardcode properties to a config file
    private final static int POOL_QUEUE_SIZE = 10;

    @Startup // Instanciated at start-time
    @ApplicationScoped // Only one instance for all your app (Singleton)
    @Produces 
    @Named("customServiceExecutor") // tag your instance, because remember that you will have also another ServiceExecutor (the default one)
    ManagedExecutor managedCustomExecutor() {

        System.out.println("customServiceExecutor created (once)!.");

        return ManagedExecutor.builder()
                .maxAsync(POOL_SIZE)
                .maxQueued(POOL_QUEUE_SIZE)
                .build();
    }

}

And then let's use it!:


import io.smallrye.context.api.NamedInstance;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.context.ManagedExecutor;


import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello/")
public class HelloResource {

    @ConfigProperty(name = "org.pjgg.greeting")
    private String greeting;

    @Inject
    @NamedInstance("customServiceExecutor")
    ManagedExecutor delorean;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        System.out.println(java.lang.Thread.activeCount());

        var promise = delorean.supplyAsync(()-> "You will see this in the future !!!");
        promise.thenAccept(System.out::println);
        System.out.println(java.lang.Thread.activeCount());

        return greeting;
    }
}

Upvotes: 1

Related Questions