Spring #Async propagate context information - java

I've a Spring Boot 2.2 application. I created a service like this:
#Async
#PreAuthorize("hasAnyRole('ROLE_PBX')")
#PlanAuthorization(allowedPlans = {PlanType.BUSINESS, PlanType.ENTERPRISE})
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
log.debug("Current tenant {}", TenantContext.getCurrentTenantId());
return new AsyncResult<AuditCdr>(auditCdrRepository.save(cdr3CXMapper.cdr3CXDtoToAuditCdr(cdrRecord)));
}
this is my #Async configuration:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
Using SecurityContextHolder.MODE_INHERITABLETHREADLOCAL I see the Security context is passed to the #Async method.
In my multi-tenant application I use a ThreadLocal to set the tenant's id:
public class TenantContext {
public final static String TENANT_DEFAULT = "empty";
private static final ThreadLocal<String> code = new ThreadLocal<>();
public static void setCurrentTenantId(String code) {
if (code != null)
TenantContext.code.set(code);
}
public static String getCurrentTenantId() {
String tenantId = code.get();
if (StringUtils.isNotBlank(tenantId)) {
return tenantId;
}
return TENANT_DEFAULT;
}
public static void clear() {
code.remove();
}
}
Because ThreadLocal is related to the thread, it's not available in the #Async method. Furthemore my custom #PlanAuthorization aop needs it to perform verifications of the tenant's plan.
Is there a clean way to set TenantContext in any #Async method in my application?

I ended up to use a TaskDecorator:
#Log4j2
public class MdcTaskDecorator implements TaskDecorator {
#Override
public Runnable decorate(Runnable runnable) {
// Right now: Web thread context !
// (Grab the current thread MDC data)
String tenantId = TenantContext.getCurrentTenantId();
Long storeId = StoreContext.getCurrentStoreId();
SecurityContext securityContext = SecurityContextHolder.getContext();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
log.info("Saving tenant information for async thread...");
return () -> {
try {
// Right now: #Async thread context !
// (Restore the Web thread context's MDC data)
TenantContext.setCurrentTenantId(tenantId);
StoreContext.setCurrentStoreId(storeId);
SecurityContextHolder.setContext(securityContext);
MDC.setContextMap(contextMap);
log.info("Restoring tenant information for async thread...");
runnable.run();
} catch (Throwable e) {
log.error("Error in async task", e);
} finally {
MDC.clear();
}
};
}
}
and I used it in this way:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("threadAsync");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
}
It works and it seems also a neat solution.

The solution for such case is to :
configure custom thread pool so that you override it's execute method to sets up your thread local (or
executes any task from your main context), decorate the task and submit decorated task for execution instead of original one
instruct #Async annotation to use concrete thread pool
#Bean("tenantExecutor")
public Executor threadLocalAwareThreadPool() {
final CustomizableThreadFactory threadNameAwareFactory =
new CustomizableThreadFactory("threadAsync");
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(500), threadNameAwareFactory) {
// override original method of thread pool
#Override
public void execute(Runnable originalTask) {
final String tenantId = tenantThreadLocal.get(); // read data from current before passing the task to async thread
// decorate the actual task by creating new task (Runnable) where you first set up the thread local and then execute your actual task
super.execute(() -> {
tenantThreadLocal.set(tenantId); // set data in actual async thread
originalTask.run();
});
}
};
return threadPoolExecutor;
}
Now we tell spring use our custom executor
#Async("tenantExecutor")
public Future<AuditCdr> saveCDR(Cdr3CXDto cdrRecord) {
// your code....
}

Instead of ThreadLocal you must use InheritableThreadLocal. Then you will see the values from the parent thread.
API Doc: https://docs.oracle.com/javase/8/docs/api/java/lang/InheritableThreadLocal.html
Here is an article about this in combination with Spring: https://medium.com/#hariohmprasath/async-process-using-spring-and-injecting-user-context-6f1af16e9759

Related

Decorator to clear thread context for Spring #Scheduled annotation

Want to clear the thread contex post execution of the functions annotated with #Scheduled in spring boot
Usage
#Scheduled(fixedDelayString = "10000")
publi void doSomething() {
}
Config for scheduled thread pool
#Bean(destroyMethod = "shutdownNow")
public ScheduledExecutorService scheduledExecutorService() {
return Executors.newScheduledThreadPool(5);
}
Have created a simple decorator to solve the same
package com.demo.decorator;
import com.demo.utils.GeneralUtils;
import org.springframework.core.task.TaskDecorator;
public class ThreadContextDecorator implements TaskDecorator {
#Override
public Runnable decorate(Runnable runnable) {
return () -> {
try {
runnable.run();
} finally {
GeneralUtils.clearContext();
}
};
}
}
Not sure how to add it in bean of ScheduledExecutorService

How to disable EnableAsync for one of the executor to wait all task finish?

I'm using #EnableAsync and #Async for enabling asynchronous call when submitting request to thread pool. This work well, as it doesn't block the flow at future.get(). Now, we added another bean processExecutor within MySpringBootApplication class. But for this we wanted to wait till it completes all the task when submitted to this executor. But actually, it continues and doesn't wait at future.get(). Can someone help me, what can I make change so that it waits for all thread to complete ?
#SpringBootApplication
#EnableAsync
public class MySpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
#Bean(name="calculateExecutor")
public TaskExecutor calExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(MySpringBootApplication.class,args);
}
}
Following is Service class
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public Future<String> process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
//API call which takes 2-3 seconds to complete
return new AsyncResult<String>(resp.getBody().getDataId())
}
}
I'm calling future.get() from other component class.

Shutdown #Bean ExecutorService using #PreDestroy

I have a Spring #Configuration class as follows:
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public BlockingQueue<Runnable> queue() {
return ArrayBlockingQueue<>(1000);
}
#Bean
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue);
}
#PreDestroy
public void shutdownExecutor() {
// no executor instance
}
}
I would also like to specify a #PreDestroy method which shuts down the ExecutorService. However, the #PreDestroy method cannot have any arguments which is why I'm not able to pass the executor bean to this method in order to shut it. Specifying destroy method in #Bean(destroyMethod = "...") does not work either. It allows me to specify existing shutdown or shutdownNow, but not the custom method which I intend to use.
I know I could instantiate the queue and executor directly and not as Spring beans, but I'd rather do it this way.
I love defining classes inline:
#Bean(destroyMethod = "myCustomShutdown")
public ExecutorService executor(BlockingQueue<Runnable> queue) {
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue) {
public void myCustomShutdown() {
...
}
};
}
Use the ThreadPoolTaskExecutor which does all that by default.
#Configuration
public class ExecutorServiceConfiguration {
#Bean
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() {
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
return new ArrayBlockingQueue<>(queueCapacity);
}
};
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setKeepAliveSeconds(0);
taskExecutor.setQueueCapacity(1000);
return taskExecutor;
}
}
This will configure the ThreadPoolExecutor and shutdown when the application stops.
If you don't need the ArrayBlockingQueue but can live with the default LinkedBlockingQueue and only need to specify the queue capacity you can remove the override createQueue method.

Is creating runnables as a Spring component a bad practice?

It is the first time i use concurrency with Spring and i have a piece of code that makes concurrent calculations using Spring. Here is the code :
#Component
public class AppScheduler {
//#Autowired DAOs or services.
private ThreadPoolExecutor executor;
private AbstractApplicationContext context;
#PostConstruct
public void init() {
executor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10));
context = new AnnotationConfigApplicationContext(VtmDispatcherConfig.class);
}
#Scheduled(fixedDelay = 60 * 1000)
//at rejection break the execution.
public void appEntry() {
//fetch some records from db
for (Record rec : records) {
try {
executeTransactionally(rec);
} catch (RejectedExecutionException ex) {
break;
}
}
}
#Transactional
//Creates runnable tasks, makes some db operations and passes task to the thread pool
private void executeTransactionally(Record rec) {
ARunnableTask aRunnableTask = (ARunnableTask) context.getBean("aRunnableTask");
aRunnableTask.setParameter(rec.param);
//some db operations...
tempTableExecutorPool.execute(aRunnableTask);
}
}
Here a scheduled code fetchs records from db and with each record instantiates a runnable task and passes it to the thread pool executor. And my runnable task is here :
#Component
#Scope("prototype")
public class ARunnableTask implements Runnable {
private String param;
private ThreadPoolExecutor executor;
private DAOOrService inject
#Autowired
public TempTableExecutorTask(DAOOrService inject) {
executor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1000));
this.inject = inject;
}
#Override
public void run() {
//...
executeTransactional();
//...
executor.shutdown();
}
#Transactional
private void executeTransactional() {
//some transactional processes
}
public void setParameter(String param) {
this.param = param;
}
}
and here is my question : Is it a good practice creating runnable tasks as a spring component and using #Transactional in a runnable?
If it is not a bad practice, is there a better way for creating runnable spring components than :
ARunnableTask aRunnableTask = (ARunnableTask) context.getBean("aRunnableTask");
aRunnableTask.setParameter(rec.param);

Spring Boot REST - requests are not executing with ThreadPoolTaskExecutor configuration

I am trying to develop a spring boot app. I have written all core implementations in core java without spring framework. I am using that jar in this spring boot app. I would like to manage the concurrency of my rest controller. So, configured ThreadPoolTaskExecutor accordingly in the main class. Ideally, I want only 2 concurrent requests to get into the execute() method, which I annotated Async. I was testing for 2 concurrent requests at a time but I see in the log that my requests are entering execute() all at once. All the tasks are memory intensive. So those are failing with heap memory issues. I am trying to figure out the ideal concurrency number. I would like to know if my configuration is correct or am I missing something? Thank you.
Here's my main class:
#SpringBootApplication
#EnableAsync
public class RestapiApplication implements AsyncConfigurer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(RestapiApplication.class, args);
System.out.println("Rightdata Middleware ready to accept requests:");
}
#Bean(name = "executor1")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setCorePoolSize(2);
taskExecutor.setThreadNamePrefix("LULExecutor-");
taskExecutor.setQueueCapacity(100);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
And here's my REST controller:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
#Async("executor1")
private void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
UPDATE- 1:
I have now changed the code to following:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
/**
* The log.
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
#Override
public void run() {
try {
execute(jsonObjectPOJO);
} catch (Exception e) {
e.getMessage();
}
}});
executor.shutdown();
return jsonObjectPOJO;
}
}
And AsyncService class:
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> futureTarget;
Future<?> futureSource;
futureSource = processSource(executorService);
futureTarget = processTarget(executorService);
manageSourceProcessingResults(futureSource);
manageTargetProcessingResults(futureTarget);
executorService.shutdown();
//Do rest of the tasks.
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processSource(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureSource = executorService.submit(coreActionClass);
return futureSource;
}
#SuppressWarnings({"unchecked", "rawtypes"})
protected Future<?> processTarget(executorService){
//Get appropriate class instance with call() - coreActionClass.
Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
return futureTarget;
}
private void manageSourceProcessingResults(Future<?> futureSource) {
try{
futureSource.get();
} catch(Exception e){
e.printStackTrace();
}
}
private void manageTargetProcessingResults(Future<?> futureTarget) {
try{
futureTarget.get();
} catch(Exception e){
e.printStackTrace();
}
}
}
My understanding is that when I configure maxpoolsize(2) no more
than 2 requests would be in the execute() method at one time. For a
new request to enter, one of the earlier requests has to complete
its execution. Is my understanding correct? Would the async apply
to the inner executor service?
I am of the view that at one time only 2 requests are handled and
each of those requests can spawn 2 different threads and complete
its task. Please clarify.
I see two problems.
1) In your process method you are creating a new ExecutorService. This is not needed. Instead just call the execute method after the jsonObjectPOJO is retrieved.
2) You cannot use #Async int he same class that it is implemented. You'll need to create a new class, lets called MyAsyncService to contain the #Async method. This is because of the aspect orientated programming that is going on under the covers.
Check out this link for more info. Below is a quote from the link.
First – let’s go over the rules – #Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won’t work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn’t work because it bypasses the proxy and calls the underlying method directly.
EDIT 1:
#RestController
#RequestMapping("/end2end")
public class End2EndRestController {
#AutoWired
AsyncService asyncService;
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(#RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
asyncService.execute(jsonObjectPOJO);
return jsonObjectPOJO;
}
public class AsyncService {
#Async("executor1")
public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
//No Futures, no ExecutorServices, just process that request.
}
}
By creating and configuring the ThreadPoolTaskExecutor to use only 2 threads, you have accomplished your goal.
EDIT2: Spring #Async limit number of threads

Categories

Resources