How to get details of Spring Boot #Async method's rejected task? - java

In my application I'm using an #Async method which is calling a rest service and based on the rest service result I'm updating the MyJob status in DB.
#Async("thatOneTaskExecutor")
public void myAsyncTask(MyJob job) {
// get job details from the job and call rest service
// update the job with the result from rest service and save updated MyJob to DB
}
I'm using Spring's ThreadPoolTaskExucutor, Below is a snap from my AsyncConfiguration class where I declared this task executor.
private ThreadPoolTaskExecutor createExecutor(String name, int core, int max, int queue) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(core);
executor.setMaxPoolSize(max);
executor.setQueueCapacity(queue);
executor.setThreadNamePrefix(name);
executor.setTaskDecorator(new MdcAwareTaskDecorator());
executor.initialize();
return executor;
}
#Bean(name = "thatOneTaskExecutor")
public Executor taskExecutor() {
String prefix = "thatOneTask-";
String corePoolSize = 12;
String maxPoolSize = 20;
String queueSize = 1000;
ThreadPoolTaskExecutor executor = createExecutor(prefix, corePoolSize, maxPoolSize, queueSize);
executor.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
return executor;
}
As you can see I had configured a RejectedExecutionHandler for my Executor.
According to Spring documentation when queue is full this method will be called.
* Method that may be invoked by a {#link ThreadPoolExecutor} when
* {#link ThreadPoolExecutor#execute execute} cannot accept a
* task. This may occur when no more threads or queue slots are
* available because their bounds would be exceeded, or upon
* shutdown of the Executor.
public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("Task Rejected because of max queue size");
// How to get info about that particular job, for which Task executor rejected this task??
}
}
Rejected execution handler is working fine for me, now inside this rejectedExecutorion method, I want to access the MyJob(parameter of my async method) for which the async task is rejected. I want to update that particular rejected job with a status so that I can later run a corn and process those rejected jobs. Inside this rejectedExecution method I only have Runnable and ThreadPoolExucutor, how can I extract/get info about MyJob here?
My application's Spring boot version is 2.2.2.RELEASE

You could consider using the TaskExecutor directly instead of the #Async annotation by implementing the Runnable interface for MyJob Class and perform the required async operation inside the run() method.
The Runnable r could be cast back to MyJob Object in the rejectedExecution method of the handler and hence you could retrieve information of your job from there.
public class Myjob implements Runnable{
.......
#Override
public void run(){
//get job details from the job and call rest service
//update the job with the result from rest service and save updated MyJob to DB
}
}
#Autowired
TaskExecutor taskExecutor;
public void myAsyncTask(MyJob job) {
taskExecutor.execute(job)
}
public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("Task Rejected because of max queue size");
if(r.getClass()==MyJob.class)
{
MyJob failedJob=(MyJob)r; //Job info
}
}
}

Related

Spring TaskScheduler does not schedule task immediatelly

I want to execute the same task couple of times but it seems that every next invocation of my code does not execute the task immediately, for example it executes after one minute.
Since user has to schedule tasks manually I use ScheduledTaskRegistrar.TaskScheduler.
taskRegistrar.getScheduler().schedule(myTask, new Date());
What could be the reason? User clicked schedule button twice on my fronted application and backend invoked the above schedule method twice as expected. First execution of my task was immediate, second run after two minutes.
UPDATE: taskregistrar config, maybe I didn't configure it at all. my tasks are added as cron tasks on application deployment. But they also must be runnable manually if user wants to trigger it. Below is more or less the whole logic:
#Configuration
#EnableAsync
#EnableScheduling
#Component
#Slf4j
#Generated
#Getter
public class ScheduleTaskService implements SchedulingConfigurer {
#Autowired
private List< MyTask> taskList;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
taskList.stream().filter(MyTask::isOn).forEach(this::addTaskToScheduler);
}
public void addTaskToScheduler(GwoTask task) {
taskRegistrar.addCronTask(task, task.getCronExpression());
}
public void scheduleImmediateInvocation(MyTask myTask) {
taskRegistrar.getScheduler().schedule(myTask, new Date());
}
}
By referring to the source code of ScheduledTaskRegistrar,
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
...
If we do not set taskScheduler, Executors.newSingleThreadScheduledExecutor() is used by default. Hence new task will be blocked by processing task.
For your use case in scheduleImmediateInvocation, I recommend to use another thread pool(Probably from Executors) instead as:
It isn't actually a schedule job.
More control on pool size is needed to suit your workload
If you just want to make ScheduledTaskRegistrar execute more concurrently, configure it as:
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// set the desired core pool size
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
this.taskRegistrar = taskRegistrar;
// ...
}

Getting "No session currently bound to execution context" when using Threadpool Executor

I am new to Dropwizard and Hibernate.
Currently I am working on an application where I need to use AsyncThreadPool for performing some time consuming operations from my main thread which needs to send a response right away.
In the synchromous flow the operations are working fine because my listener has #UnitOfWork annotation in place. But in the threadpool spawned thread I am getting "No session currently bound to execution context" error every time.
I tried #UnitOfWork annotation on thread class as well as the run() method, as the documentation says that I have to use this annotation from the resource accessing the instance.
Here is my runnable class.
public class CallbackTask implements Runnable{
Snapshot baseSnapshot;
ProcessorResponse processorResponse;
Job job;
ElasticSearchDAO esDao;
OrchestratorJobService orchestratorJobService;
Snapshot snapshot;
RestoreHelper restoreHelper;
String indexName;
public CallbackTask(Snapshot baseSnapshot, ProcessorResponse processorResponse, Job job, ElasticSearchDAO esDao, OrchestratorJobService orchestratorJobService, Snapshot snapshot, RestoreHelper restoreHelper, String indexName) {
this.baseSnapshot = baseSnapshot;
this.processorResponse = processorResponse;
this.job = job;
this.esDao = esDao;
this.orchestratorJobService = orchestratorJobService;
this.snapshot = snapshot;
this.restoreHelper = restoreHelper;
this.indexName = indexName;
}
#Override
#Transactional
public void run() {
int retryCount = job.getRetrialCount()==null ? 0: job.getRetrialCount();
if(retryCount< JOB_RETRY_COUNT){
job.setRetrialCount(++retryCount);
//orchestratorJobService.runConfig(job.getConfigId(), null);
}else{
snapshot.setSoftDelete(true);
}
orchestratorJobService.persistData(job, snapshot);
}
Thanks in advance for any help.
Make sure that you're using a managed ExecutorService (i.e. do not create it yourself, use the built-in methods):
public class MyApplication extends Application<MyConfiguration> {
#Override
public void run(MyConfiguration configuration, Environment environment) {
ExecutorService executorService = environment.lifecycle()
.executorService(nameFormat)
.maxThreads(maxThreads)
.build();
ScheduledExecutorService scheduledExecutorService = environment.lifecycle()
.scheduledExecutorService(nameFormat)
.build();
}
}
#UnitOfWork uses the current session context strategy, so you need to put a session to the current thread context
Dropwizard #UnitOfWork with asynchronous database call

Java Spring boot async method isn't being called occasionally

I have a service with a method annotated with #Async as below:
#Service("AsyncService")
public class AsyncService {
#Async
public void asyncPrint() {
logger.info("Inside asyncPrint");
}
}
The async method is called from another service like this:
#Service("CallerService")
public class CallerService {
#Autowired
private AsyncService asyncService;
public void caller() {
logger.info("Before asyncPrint");
asyncService.asyncPrint();
logger.info("After asyncPrint");
// Custom code
}
}
However, I'm seeing that sometimes the async method is not being called. I can see the before and after logs, but the async log is not printed.
Sample o/p:
//Logs from other services
Before asyncPrint
After asyncPrint
Inside asyncPrint
//Logs from other services
Before asyncPrint
After asyncPrint
Why could this be happening? Can async tasks be dropped if there is a backlog in the Executor? Also, is there a way for me to see how many of the total running threads are being used by the async? Or how else can I debug this?
Thanks.
The log doesn't mean your Async method isn't being called, it means the Executor hasn't called it yet. The #Async uses a ThreadPoolTaskExecutor by default with unlimited queue capacity. This means ALL your tasks will be queued until processing threads become available.
If you want to increase your processing Threads you can defined a custom ThreadPoolTaskExecutor bean as below.
#Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
Where you can play with corePoolSize and maxPoolSize according to your hardware specifications.

How to get the queue size of the executor in real time

Supposed i have this application.java
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(50);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(5000);
executor.setThreadNamePrefix("sm-async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
}
My goal is to create an alert if the current real time queue size of the async executor is in 80% or nearly the limit. I think we can get the value from executor.getThreadPoolExecutor().getQueue().size();. Im currently stuck on how to achieve that
#Controller
public class QueueMonitorController {
#Autowired
private Executor executor;
#RequestMapping(value = "/queuesize", method = RequestMethod.GET)
public int queueSize() {
ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor;
return tpe.getQueue().size();
}
}
If you can provide the bean as a ThreadPoolExecutor, then you don't even need the cast. The internal implementation of size() in LinkedBlockingQueue (which ThreadPoolExecutor uses) is AtomicInteger.get().
So there's no need to get creative and build your own mechanisms, it's all built-in. Based on Spring 4.2, but shouldn't depend on the version too much.
So the root goal is to monitor the queue, and send an alert when queue is 80% full. This should not go into your code which is responsible for making sure that your business logic works. You shouldn't make hacks there to account for lack of resources. If the idea is that you should throttle users when the queue is packed, there are far better ways to handle those.
Since the idea is to do "light monitoring", i.e. there's no attempt to handle a case when queue is 80% full, a polling solution would be lightweight enough. Considering that the executor can be easily injected to a separate Controller, it won't even mess up your "real" code.
As ThreadPoolTaskExecutor does not expose any API you can get the queue used by it. However, you are free to extend ThreadPoolTaskExecutor and create a CustomThreadPoolTaskExecutor override the createQueue.
public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor{
private BlockingQueue<Runnable> queue;
#Override
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
queue = super.createQueue(queueCapacity);
return queue;
}
public BlockingQueue<Runnable> getQueue(){
return queue;
}
}
Now you can create asyncExecutor like below :
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
//set other properties
executor.initialize();
return executor;
}
Your CustomThreadPoolTaskExecutor executor has public method getQueue and you can use that to get the queue.
I don't know from where you have got ThreadPoolTaskExecutor class type of executor. But in java you can typecast to ThreadPoolExecutor and get queue and it's size as below:
ThreadPoolExecutor executorService = (ThreadPoolExecutor )Executors.newCachedThreadPool();
executorService.getQueue().size()
To do this in real-time as you're asking for is not so easy. You'll need to decorate the methods of BlockingQueue so that you can add code to execute immediately when the content of the queue changes.
You can then provide your queue to Spring's ThreadPoolTaskExecutor like this:
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {
#Override
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
// create and return your instance of blocking queue here
}
};

Spring batch - how to dynamically increase and decrease threads using ThreadPoolTaskExecutor

I'm fairly new to Spring-Batch so this may be a lack of understanding on my part. I'm wanting to understand how to dynamically increase and decrease threads using the ThreadPoolTaskExecutor in conjunction with the ThreadPoolExecutor while my job is running. I've tried to subclass both the ThreadPoolTaskExecutor and the ThreadPoolExecutor so I can gain access to the beforeExecute() and afterExecute() which would allow me to terminate threads if the corepoolsize was decreased using an approach that is listed on this site.
What I seem to be not understanding is that when I override the initializeExecutor() method which returns an ExecutorService, it apparently does not set the (private internal) threadPoolExecutor variable in the parent class (ThreadPoolTaskExecutor). It sets the private ExecutorService executor; (from the ExecutorConfigurationSupport class)
Since the threadPoolExecutor is not a protected member I cannot gain access to it. Without that being set, when I run I obviously end up getting a "ThreadPoolExecutor not initialized" error within the Spring Framework when I examine what's wrong under the covers.
public class MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor
{
#Override
protected ExecutorService initializeExecutor(ThreadFactory tf, RejectedExecutionHandler reh)
{
BlockingQueue <Runnable> queue = createQueue(Integer.MAX_VALUE);
MyThreadPoolExecutor tp_executor = new MyThreadPoolExecutor( this.getCorePoolSize(), this.getMaxPoolSize(), this.getKeepAliveSeconds(), TimeUnit.SECONDS, queue);
// if you look at the parent class(ThreadPoolTaskExecutor) it performs this call next.
// this.threadPoolExecutor = executor;
// that is a private member with no ability to set via any methods.
return tp_executor;
}
}
public class MyThreadPoolExecutor extends ThreadPoolExecutor
{
public MyThreadPoolExecutor(int corePoolSize, int maxPoolSize, long keepAliveTimeout, TimeUnit timeunit, BlockingQueue<Runnable> workQueue, ThreadFactory tf, RejectedExecutionHandler reh)
{
super(corePoolSize, maxPoolSize, keepAliveTimeout, timeunit, workQueue, tf, reh);
}
protected void beforeExecute (final Thread thread, final Runnable job)
{
...
}
}
Can someone explain what I am missing in my approach?
I assume you want to use one number of threads in one job step and another number of threads in another job step. Simple way to achieve that would be to declare two separate executors with necessary number of threads, zero corePoolSize (to not create threads when this is not necessary) and zero keepAliveSeconds (to not keep threads when this is no longer necessary). Then just inject first executor in one step and second executor in another step.
#Configuration
public class Conf {
#Bean
public TaskExecutor executorA(#Value("${first.number.of.threads}") int numberOfThreads) {
return executor(numberOfThreads);
}
#Bean
public TaskExecutor executorB(#Value("${second.number.of.threads}") int numberOfThreads) {
return executor(numberOfThreads);
}
private TaskExecutor executor(int numberOfThreads) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(0);
executor.setMaxPoolSize(numberOfThreads);
executor.setAllowCoreThreadTimeOut(true);
executor.setKeepAliveSeconds(0);
return executor;
}
}

Categories

Resources