3

We have a Spring application, I want to add a service that will handle 10Ks IDs with multiple threads but will be as background process without impact production realtime.

Service will update database and send external providers requests.

I don't want service to impact/effect production performance/timing, I want to execute operation on each ID in a low priority

I read previous post about setting priority in Executer, but I want low priority to all other threads that can be outside this specific Executer scope.

Is answer using ThreadPoolExecutor more relevant to my case?

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, numOfWorkerThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.setThreadFactory(new OpJobThreadFactory(Thread.NORM_PRIORITY-2));

public final static class OpJobThreadFactory implements ThreadFactory {
 private int priority;
 public OpJobThreadFactory(int priority) {
  this(priority, true);
}

@Override
public Thread newThread(Runnable r) {
  Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
  t.setDaemon(daemon);
  t.setPriority(priority);
 }
}
  • maybe even use Thread.MIN_PRIORITY

Or I'm fine with using Executors.newCachedThreadPool()

Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.

Also should I use Spring bean? because I need to create pool on demand/request so it seems not needed/wrong

EDIT Should I use Spring Actuator to get this task or other monitoring tool?

Spring Boot Actuator module helps you monitor and manage your Spring Boot application by providing production-ready features like health check-up, auditing, metrics gathering, HTTP tracing etc. All of these features can be accessed over JMX or HTTP endpoints.

Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • Since you're manually creating `Thread`s rather than `Runnable`s that you submit to an `ExecutorService` (although that would be preferable in general), why don't you just use its `setPriority()`? – daniu Sep 09 '19 at 11:49
  • @daniu isn't the priority effect threads created by specific `ExecutorService` and not all system? – Ori Marko Sep 09 '19 at 11:52
  • "Service will update database and send external providers requests." it is most likely that the database will affect your system performance more than your code. And assigning priorities to database queries is a completely different and not easy task. – Mikhail Antonov Sep 18 '19 at 11:52
  • @MikhailAntonov lets handle one bounty at a time, can I have such a low priority API? – Ori Marko Sep 18 '19 at 11:55
  • @user7294900 Do you really need this service running for 24 hours ? b4 going into technical aspects, make sure you can reduce the load functionaly – Shubham Kadlag Sep 21 '19 at 16:31
  • @ShubhamKadlag 24H, yes – Ori Marko Sep 21 '19 at 16:57

2 Answers2

3

I would like to throw some light on the question

what is a thread priority? According to java SE Docs

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority.

Even though you create threads with priority it does not completely guarantee that threads with lower priority get executed first you may have to block the thread with lower priority until other threads are executed

For small java programs you can handle the thread execution by yourself but for larger programs, it's recommended either you use the Executor Framework from vanilla Java which is from the package java.util.concurrent or use the spring TaskExecutor.By using both frameworks you can execute tasks asynchronously that is executing them in the background as part of the main task.

Impact on Production:

The main task, for example, will be a call to your rest endpoint i.e /account and on calling the account endpoint you want to send welcome emails to customers which are a third party API call which can be executed asynchronously either using Executor Framework or Spring TaskExecutor on executing them asynchronously i.e as background process they will not have an impact on the current API but it will surely have an impact on the production server since you are running the threads within the same JVM and they share common memory. if there are a number of threads created and not destroyed .you server will surely go down.

So using Executor Framework or Spring TaskExecutor does not guarantee you that it will not affect your current production it will surely increase the performance of the rest API that is called. since it's executed asynchronously and on your other questions

can i use Executors.newCachedThreadPool()

yes if you have a number of the short-lived task such as updating a single column in a database or triggering a rest endpoint only once and it's not suitable for bulk loading or executing some backend job which updates 10000 records because it will create larger number of threads for each task and you will surely face memory problems.

Also, should I use Spring bean? because I need to create a pool on-demand/request so it seems not needed/wrong

yes you can ThreadPoolTaskExecutor use https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html as per the docs Setting "queueCapacity" to 0 mimics Executors.newCachedThreadPool() .so you can use either Spring TaskExecutor or use Executor Framework from vanilla Java I personally recommend using Spring TaskExecutor which has more feature for a good start on using Spring you can refer the tutorial https://egkatzioura.com/2017/10/25/spring-and-async/ which is a good start

final verdict: if you are looking to execute the task only asynchronous or a background process you can use either use Executor Framework from java or Spring TaskExecutor but both will have an impact on production since they use the same JVM .if you do not want to impact production at all then I recommend creating separate spring boot app on a different server and make the database calls or service call from the new app and expose it as a rest endpoint and invoke these endpoints asynchronously from your main app using Spring Task Executor.

Spring Task Executor: https://egkatzioura.com/2017/10/25/spring-and-async/

Java Executor Framework : https://stackabuse.com/concurrency-in-java-the-executor-framework/

for using threads with low priority : https://medium.com/@daniyaryeralin/priority-based-thread-pooling-in-spring-framework-d74b91b51dcb

Sorry if the answer is too long :)

Richard Elite
  • 720
  • 5
  • 15
1

Here there is a nice tutorial about priority based task execution in Spring. Maybe this may help you in some ways.

This is a method of creating a configurable ¨heap¨ of task and always keep your main task in the top of the heap.

To sum up this process you should create a custom Task Executor. Firstly you need to create a ThreadPoolTaskExecutor bean with one method being overidden. The properties that should be modified are: CorePoolSize(initial number of threads), QueueCapacity(the number of threads waiting in the queue), and MaxPoolSize(maximum number of threads). With these parameters you can configure your applications limitations in order for this service not to impact the production performance.

 @Bean("CustomTaskExecutor")
  public TaskExecutor threadPoolTaskExecutor(
          @Value("${spring.async.core-pool-size}") int corePoolSize,
          @Value("${spring.async.max-pool-size}") int maxPoolSize,
          @Value("${spring.async.queue-capacity}") int queueCapacity) {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {

      @Override
      protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
        return new PriorityBlockingQueue<Runnable>(queueCapacity);
      }

    };
    executor.setCorePoolSize(corePoolSize);
    executor.setMaxPoolSize(maxPoolSize);
    executor.setQueueCapacity(queueCapacity);
    return executor;
  }

After that,you need to make tasks with priorities that the task executor can understand. For that we would need to create two classes: 1) A custom class that implements Runnable interface that will be running the task 2) A wrapper class that extends FutureTask and implementsComparable interface, so that the task executor could understand the priority picking logic of the tasks

public class Task implements Runnable {
    private Consumer<Job> jobConsumer;
    private Job job;
      public Job getJob() {
        return this.job;
      }
      public Task(Consumer<Job> jobConsumer, Job job) {
        this.jobConsumer = jobConsumer;
        this.job = job;
      }
      @Override
      public void run() {
        this.jobConsumer.accept(job);
      }
    }

Then you have the FutureCustomTask class:

public class FutureCustomTask extends FutureTask<FutureCustomTask> implements Comparable<FutureCustomTask> {
private Task task;
public FutureCustomTask(Task task) {
    super(task, null);
    this.task = task;
  }
@Override
  public int compareTo(FutureCustomTask o) {
    return task.getJob().getPriority().compareTo(o.task.getJob().getPriority());
  }
}

For the execution the TaskExecutor needs to be Autowired. Then, you can create your Task object, wrap it inside FutureCustomTask, and pass it to TaskExecutor.The code should look like this:

@Autowired
private TaskExecutor taskExecutor;
@Autowired
private JobBusiness jobBusiness;
...
Task task = new Task(jobBusiness::performSomethingOn, job);
taskExecutor.execute(new FutureCustomTask(task));
Andrei Tigau
  • 2,010
  • 1
  • 6
  • 17
  • Can you give more specific answer? – Ori Marko Sep 17 '19 at 19:07
  • I edited my answer with a more detailed explanation. The main idea is that you can create a custom TaskExecutor, which can be useful regarding your performance concerns – Andrei Tigau Sep 18 '19 at 11:08
  • Sorry, but I don't get how it won't effect production performance – Ori Marko Sep 19 '19 at 06:11
  • It won´t be noxious for your performance because you can customize those parameters: maxPoolSize, queueCapacity. So you can play around thouse values and test them. If you feel performance is affected, you can always decrease the parameters values – Andrei Tigau Sep 19 '19 at 07:20
  • 1
    This is still supposed to run in the same JVM as that of the main application and will have an impact. Unless the application server has a high availability of 'leftover' resources, the impact will definitely be felt. The threads' priority will only be a suggestion and will not be "low" just because we mention it. One alternative that I can think of is to create this as a separate spring boot project and allow it to connect to the database separately. It can expose its APIs to the main application for consumption. – Sid Sep 20 '19 at 19:25