I am using Java's CompletableFuture like this into a spring boot #Service:
#Service
public class ProcessService {
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(3);
#Autowired
ChangeHistoryService changeHistoryService;
public Attribute process(Attribute attribute) {
//some code
CompletableFuture.runAsync(() -> changeHistoryService.logChanges(attribute), EXECUTOR);
return attribute;
}
}
The process method is called form a method inside a #RestController:
#RestController
public class ProcessController {
#Autowired
ProcessService processService;
#RequestMapping(value = "/processAttribute",
method = {RequestMethod.POST},
produces = {MediaType.APPLICATION_JSON_VALUE},
consumes = {MediaType.APPLICATION_JSON_VALUE})
public Attribute applyRules(#RequestBody Attribute attribute) {
Attribute resultValue = processService.service(attribute);
return resultValue;
}
}
ChangeHistoryService::logChanges only save some data to database according to its parameter.
I have a microservice that makes a number of request to this "/processAttribute" endpoint and print all responses.
When I put a breakpoint in logChanges method, the microservice is waiting on some request but not all which makes me think that the ChangeHistoryService::logChanges not always runs async. If I don't supply the runAsync with a ExecutorService, the microservice blocks on more request but still not all.
From what I understood this is because method that process the request and logChanges method share same thread pool (ForkJoinPool?).
Anyway, as I have another ExecutorService, logChanges should not runs independently? Or is something about how IDE treats breakpoints on async task? I am using IntelliJ IDEA.
The problem was that the breakpoint suspends all threads and not only the thread that runs logChanges method. I fix this in Intellij IDEA by pressing right click on breakpoint and checked "Thread" checkbox, not "All":
You have a rather small threadpool, so it's no wonder that you can saturate it. The threads that process requests are not the same as the ones processing your CompletableFutures. One is an internal component of the server, and the second one is the one you explicitly created, EXECUTOR.
If you want to increase the asynchronousness, try giving EXECUTOR some more threads and see how the behaviour changes accordingly. Currently the EXECUTOR is a bottleneck, since there are far more threads available for requests to run in.
Note that by putting a breakpoint inside logChanges() you'll be blocking one thread in the pool, making it even more saturated.
Related
I have a method
#Service
public class MyService {
public Mono<Integer> processData() {
... // very long reactive operation
}
}
In the normal program flow, I call this method asynchronously via a Kafka event.
For testing purposes I need to expose the method as a web service, but the method should be exposed as asynchronous: returning only HTTP code 200 OK ("request accepted") and continuing the data processing in the background.
Is it OK (= doesn't it have any unwanted side effects) just to call Mono#subscribe() and return from the controller method?
#RestController
#RequiredArgsConstructor
public class MyController {
private final MyService service;
#GetMapping
public void processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
}
}
Or is it better to do it like this (here I am confused by the warning from IntelliJ, maybe the same as https://youtrack.jetbrains.com/issue/IDEA-276018 ?):
public Mono<Void> processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe(); // IntelliJ complains "Inappropriate 'subscribe' call" but I think it's a false alarm in my case(?)
return Mono.empty();
}
Or some other solution?
Is it OK (= doesn't it have any unwanted side effects) just to call Mono#subscribe() and return from the controller method?
There are side effects, but you may be ok living with them:
It truly is fire and forget - which means while you'll never be notified about a success (which most people realise), you'll also never be notified about a failure (which far fewer people realise.)
If the process hangs for some reason, that publisher will never complete, and you'll have no way of knowing. Since you're subscribing on the bounded elastic threadpool, it'll also tie up one of those limited threads indefinitely too.
The first point you might be fine with, or you might want to put some error logging further down that reactive chain as a side-effect somehow so you at least have an internal notification if something goes wrong.
For the second point - I'd recommend putting a (generous) timeout on your method call so it at least gets cancelled if it hasn't completed in a set time, and is no longer hanging around consuming resources. If you're running an asynchronous task, then this isn't a massive issue as it'll just consume a bit of memory. If you're wrapping a blocking call on the elastic scheduler then this is worse however, as you're tying up a thread in that threadpool indefinitely.
I'd also question why you need to use the bounded elastic scheduler at all here - it's used for wrapping blocking calls, which doesn't seem to be the foundation of this use case. (To be clear, if your service is blocking then you should absolutely wrap it on the elastic scheduler - but if not then there's no reason to do so.)
Finally, this example:
public Mono<Void> processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
return Mono.empty();
}
...is a brilliant example of what not to do, as you're creating a kind of "imposter reactive method" - someone may very reasonably subscribe to that returned publisher thinking it will complete when the underlying publisher completes, which obviously isn't what's happening here. Using a void return type and thus not returning anything is the correct thing to do in this scenario.
Your option with the following code is actually ok:
#GetMapping
public void processData() {
service.processData()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
}
This is actually what you do in a #Scheduled method which simply returns nothing and you explicitly subscribe to the Mono or Flux so that elements are emitted.
I have a scenario where the spring-boot application have to download a file from downstream application and pass it to the client. The API also needs to update a read flag in the database without blocking the response(main-thread).
A basic async use-case is what I thought of and implemented in the respective API. But, I am getting a behavioral issue with #Async. The annotation is able to spawn a new thread , but its blocking the main-thread and holding the response. The expectation was to return without holding the main-thread.
Actually, the async update is the last operation of main-thread, and I guess due to that #Async is blocking the main-thread.
Can anyone please suggest a better solution of this scenario.
Calling class
ResponseEntity<byte[]> parsedResponse = retrieverService.retrieve(id,"html");
retrieverService.update(id);
return parsedResponse;
Async method
#Override
#Async("updateTaskExecutor")
public void update(String id) {
LOG.info("Updating data for metaTagId: {}", id);
db.updateReadFlag(id);
}
Async Config
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean(name = "updateTaskExecutor")
public Executor updateTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("UpdateTaskClient-");
executor.initialize();
return executor;
}
}
The Configurations were correct. I was using debugger to check the parallelism. As suggested by #M. Deinum, its not the correct way to check parallelism. After using Thread.sleep() , I could see that asynchronous calls are working as expected. I am able to send the response back, while performing an update query asynchronously.
I'm having a #RestController webservice method that might block the response thread with a long running service call. As follows:
#RestController
public class MyRestController {
//could be another webservice api call, a long running database query, whatever
#Autowired
private SomeSlowService service;
#GetMapping()
public Response get() {
return service.slow();
}
#PostMapping()
public Response get() {
return service.slow();
}
}
Problem: what if X users are calling my service here? The executing threads will all block until the response is returned. Thus eating up "max-connections", max threads etc.
I remember some time ago a read an article on how to solve this issue, by parking threads somehow until the slow service response is received. So that those threads won't block eg the tomcat max connection/pool.
But I cannot find it anymore. Maybe somebody knows how to solve this?
there are a few solutions, such as working with asynchronous requests. In those cases, a thread will become free again as soon as the CompletableFuture, DeferredResult, Callable, ... is returned (and not necessarily completed).
For example, let's say we configure Tomcat like this:
server.tomcat.max-threads=5 # Default = 200
And we have the following controller:
#GetMapping("/bar")
public CompletableFuture<String> getSlowBar() {
return CompletableFuture.supplyAsync(() -> {
silentSleep(10000L);
return "Bar";
});
}
#GetMapping("/baz")
public String getSlowBaz() {
logger.info("Baz");
silentSleep(10000L);
return "Baz";
}
If we would fire 100 requests at once, you would have to wait at least 200 seconds before all the getSlowBar() calls are handled, since only 5 can be handled at a given time. With the asynchronous request on the other hand, you would have to wait at least 10 seconds, because all requests will likely be handled at once, and then the thread is available for others to use.
Is there a difference between CompletableFuture, Callable and DeferredResult? There isn't any difference result-wise, they all behave the similarly.
The way you have to handle threading is a bit different though:
With Callable, you rely on Spring executing the Callable using a TaskExecutor
With DeferredResult you have to to he thread-handling by yourself. For example by executing the logic within the ForkJoinPool.commonPool().
With CompletableFuture, you can either rely on the default thread pool (ForkJoinPool.commonPool()) or you can specify your own thread pool.
Other than that, CompletableFuture and Callable are part of the Java specification, while DeferredResult is a part of the Spring framework.
Be aware though, even though threads are released, connections are still kept open to the client. This means that with both approaches, the maximum amount of requests that can be handled at once is limited by 10000, and can be configured with:
server.tomcat.max-connections=100 # Default = 10000
in my opinion.the async may be better for the sever.for this particular api, async not works well.the clients also hold the connections. finally it will eating up "max-connections".you can send the request to messagequeue(kafka)and return success to clients. then you get the request and pass it to the slow sevice.
I am currently developing an application with SpringBoot 2, spring-boot-starter-webflux on netty and jOOQ.
Below is the code that I have come up with after hours of research and stackoverflow searches. I have built in a lot of
logging in order to see what's happening on which thread.
UserController:
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Mono<ResponseEntity<Integer>> createUser(#RequestBody ImUser user) {
return Mono.just(user)
.map(it -> {
logger.debug("Receiving request on thread: " + Thread.currentThread().getName());
return it;
})
.map(userService::create)
.map(it -> {
logger.debug("Sending response on thread: " + Thread.currentThread().getName());
return ResponseEntity.status(HttpStatus.CREATED).body(it);
})
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
}
UserService:
public int create(ImUser user) {
return Mono.just(user)
.subscribeOn(Schedulers.elastic())
.map(u -> {
logger.debug("UserService thread: " + Thread.currentThread().getName());
return imUserDao.insertUser(u);
})
.block();
}
UserDao:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public int insertUser(ImUser user) {
logger.debug("Insert DB on thread: " + Thread.currentThread().getName());
return dsl.insertInto(IM_USER,IM_USER.VERSION, IM_USER.FIRST_NAME, IM_USER.LAST_NAME, IM_USER.BIRTHDATE, IM_USER.GENDER)
.values(1, user.getFirstName(), user.getLastName(), user.getBirthdate(), user.getGender())
.returning(IM_USER.ID)
.fetchOne()
.getId();
}
The code works as expected, "Receiving request" and "Sending response" both run on the same thread (reactor-http-server-epoll-x)
while the blocking code ( the call to imUserDao.insertUser(u) ) runs on an elastic Scheduler thread (elastic-x).
The transaction is bound to the thread on which the annotated method is called (which is elastic-x) and thus works as expected (I have tested
it with a different method which is not posted here, to keep things simple).
Here is a log sample:
20:57:21,384 DEBUG admin.UserController| Receiving request on thread: reactor-http-server-epoll-7
20:57:21,387 DEBUG admin.UserService| UserService thread: elastic-2
20:57:21,391 DEBUG admin.ExtendedUserDao| Insert DB on thread: elastic-2
20:57:21,393 DEBUG tools.LoggerListener| Executing query
...
20:57:21,401 DEBUG tools.StopWatch| Finishing : Total: 9.355ms, +3.355ms
20:57:21,409 DEBUG admin.UserController| Sending response on thread: reactor-http-server-epoll-7
I have researched reactive programming for a long time now, but never quite got to program anything reactive. Now that I am, I am wondering if I am doing it correctly.
So here are my questions:
1. Is the code above a good way to handle incoming HTTP requests, query the DB and then respond?
Please ignore the logger.debug(...) calls which I have built in for the sake of my sanity :) I kind of expected to have a Flux< ImUser> as the argument to the controller method, in the sense that I have a stream of multiple potential requests
that will come at some point and will all be handled in the same way. Instead, the examples that I have found create a Mono.from(...); every time a request comes in.
2. The second Mono created in the UserService ( Mono.just(user) ) feels somewhat awkward. I understand that I need to start a new stream to be able to
run code on the elastic Scheduler, but isn't there an operator that does this?
3. From the way the code is written, I understand that the Mono inside the UserService will be blocked until the DB operation finishes,
but the original stream, which serves the requests, isn't blocked. Is this correct?
4. I plan to replace Schedulers.elastic() with a parallel Scheduler where I can configure the number of worker threads. The idea is that the number of maximum worker threads should be the same as maximum DB connections.
What will happen when all worker threads inside the Scheduler will be busy? Is that when backpressure jumps in?
5. I initially expected to have this code inside my controller:
return userService.create(user)
.map(it -> ResponseEntity.status(HttpStatus.CREATED).body(it))
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
but I have not been able to achieve that AND keep the things running in the correct threads. Is there any way to achieve this inside my code?
Any help would be greatly appreciated. Thanks!
Service and Controller
The fact that your service is blocking is problematic, because then in the controller you are calling a blocking method from inside a map that isn't moved on a separate thread. This has the potential to block all controllers.
What you could do instead is return a Mono from UserService#create (remove the block() at the end). Since the service ensures that the Dao method call is isolated, it is less problematic. From there, no need to do Mono.just(user) in the Controller: just call create and start chaining operators directly on the resulting Mono:
#RequestMapping(value = "/user", method = RequestMethod.POST)
public Mono<ResponseEntity<Integer>> createUser(#RequestBody ImUser user) {
//this log as you saw was executed in the same thread as the controller method
logger.debug("Receiving request on thread: " + Thread.currentThread().getName());
return userService.create(user)
.map(it -> {
logger.debug("Sending response on thread: " + Thread.currentThread().getName());
return ResponseEntity.status(HttpStatus.CREATED).body(it);
})
.mapError(DuplicateKeyException.class, e -> new SomeSpecialException(e.getMessage(), e));
}
Logging
Note that if you want to log something there are a couple better options than doing a map and returning it:
doOnNext method is tailored for that: react to one of the reactive signals (in this instance, onNext: a value is emitted) and perform some non-mutating action, leaving the output sequence exactly the same as the source sequence. The "side-effect" of the doOn can be writing to the console or incrementing statistic counters for instance... There's also doOnComplete, doOnError, doOnSubscribe, doOnCancel, etc...
log simply logs all events in the sequence above it. It will detect if you use SLF4J and use the configured logger at DEBUG level if so. Otherwise it'll use the JDK Logging features (so you also need to configure that to display DEBUG level logs).
A word about transactions or rather anything relying on ThreadLocal
ThreadLocal and thread-stickiness can be problematic in reactive programming, because there's less guarantee of the underlying execution model staying the same throughout a whole sequence. A Flux can execute in several steps, each in a different Scheduler (and so thread or thread pool). Even at a specific step, one value could be processed by thread A of the underlying thread pool while the next one, arriving later on, would be processed on thread B.
Relying on Thread Local is less straightforward in this context, and we are currently actively working on providing alternatives that fit better in the reactive world.
Your idea of making a pool of the size of the connection pool is good, but not necessarily sufficient, with the potential of several threads being used by a transactional flux, thus maybe polluting some threads with the transaction.
What happens when a pool runs out of threads
If you are using a particular Scheduler to isolate blocking behavior like here, once it runs out of threads it would throw a RejectedExecutionException.
I am trying to get the FacesContext by calling FacesContext.getCurrentInstance() in the run() method of a Runnable class, but it returns null.
public class Task implements Runnable {
#Override
public void run() {
FacesContext context = FacesContext.getCurrentInstance(); // null!
// ...
}
}
How is this caused and how can I solve it?
The FacesContext is stored as a ThreadLocal variable in the thread responsible for the HTTP request which invoked the FacesServlet, the one responsible for creating the FacesContext. This thread usually goes through the JSF managed bean methods only. The FacesContext is not available in other threads spawned by that thread.
You should actually also not have the need for it in other threads. Moreover, when your thread starts and runs independently, the underlying HTTP request will immediately continue processing the HTTP response and then disappear. You won't be able to do something with the HTTP response anyway.
You need to solve your problem differently. Ask yourself: what do you need it for? To obtain some information? Just pass that information to the Runnable during its construction instead.
The below example assumes that you'd like to access some session scoped object in the thread.
public class Task implements Runnable {
private Work work;
public Task(Work work) {
this.work = work;
}
#Override
public void run() {
// Just use work.
}
}
Work work = (Work) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("work");
Task task = new Task(work);
// ...
If you however ultimately need to notify the client e.g. that the thread's work is finished, then you should be looking for a different solution than e.g. adding a faces message or so. The answer is to use "push". This can be achieved with SSE or websockets. A concrete websockets example can be found in this related question: Real time updates from database using JSF/Java EE. In case you happen to use PrimeFaces, look at
<p:push>. In case you happen to use OmniFaces, look at <o:socket>.
Unrelated to the concrete problem, manually creating Runnables and manually spawning threads in a Java EE web application is alarming. Head to the following Q&A to learn about all caveats and how it should actually be done:
Spawning threads in a JSF managed bean for scheduled tasks using a timer
Is it safe to start a new thread in a JSF managed bean?