Hello,

We are in the process to upgrade an application from Camel 4.6.0 to Camel
4.9.0
We are using Camel Spring Boot and the associated automatically configured
SpringBootPlatformHttpEngine to expose a REST api

We noticed that in 4.9.0 (started in 4.8.x branch apparently), this engine
starts the Camel route asynchronously from the receiving WebServer thread,
delegating the execution to a ThreadPoolTaskExecutor created by Spring Boot.
( Quick note : as the default ThreadPoolTaskExecutor created by Spring Boot
as only 8 threads (configurable), the change is quite radical compared to
the number of thread in a classical WebServer thread pool, it would be nice
to recommend to users to augment the size of it if they need to, I didn't
find much resources about that )

We also noticed that in the following commit, two level of asynchronism as
being introduced :
https://github.com/apache/camel-spring-boot/commit/794c5a316447d1bc818b5e1a421c8145ba15a473

*
org.apache.camel.component.platform.http.springboot.SpringBootPlatformHttpConsumer#service
(called by Spring via
org.apache.camel.component.platform.http.springboot.CamelRequestHandlerMapping)
  is wrapped inside a asynchronous-executing proxy (equivalent to Spring
@Async)
* and then SpringBootPlatformHttpConsumer is creating a CompletableFuture
executed asynchronously in the same ThreadPoolTaskExecutor

What was the purpose of having those two levels ? Any single of them would
have not be enough ?

If we are not mistaken,
it appears that both are not playing well together, resulting in the usage
of two threads of the ThreadPoolTaskExecutor during the execution of the
Camel route instead of only one.

In particular org.springframework.aop.interceptor.AsyncExecutionInterceptor
was not designed to call a method which return a "real asynchronous"
CompletableFuture
( I think this spring issue described exactly that :
https://github.com/spring-projects/spring-framework/issues/19964 )

More precisely : AsyncExecutionInterceptor is waiting synchronously for the
CompletableFuture returned by SpringBootPlatformHttpConsumer#service to
complete :
https://github.com/spring-projects/spring-framework/blob/main/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java#L114


Please find below a extract of a thread dump just before the end of the
single execution of a REST route where we can see that two thread from the
Spring ThreadPoolTaskExecutor are occupied :
(I customize the thread prefix to "spring-task" to highlight that point ) :
- spring-task-2 is finishing the sending of the response after the route
execution
- spring-task-1 is just waiting for the CompletableFuture executed by
spring-task-2


"spring-task-2@13482" tid=0x43 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
 at
org.apache.camel.component.platform.http.springboot.SpringBootPlatformHttpConsumer.handleService(SpringBootPlatformHttpConsumer.java:133)
 at
org.apache.camel.component.platform.http.springboot.SpringBootPlatformHttpConsumer.lambda$service$0(SpringBootPlatformHttpConsumer.java:84)
 at
org.apache.camel.component.platform.http.springboot.SpringBootPlatformHttpConsumer$$Lambda/0x000001679aa98ed0.run(Unknown
Source:-1)
 at
java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1804)
 at
java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:-1)
 at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
 at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
 at java.lang.Thread.runWith(Thread.java:1596)
 at java.lang.Thread.run(Thread.java:1583)

"spring-task-1@13467" tid=0x42 nid=NA waiting
  java.lang.Thread.State: WAITING
 at jdk.internal.misc.Unsafe.park(Unsafe.java:-1)
 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:221)
 at
java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1864)
 at java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3780)
 at java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3725)
 at
java.util.concurrent.CompletableFuture.waitingGet(CompletableFuture.java:1898)
 at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2072)
 at
org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
 at
org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda/0x000001679aa988a8.call(Unknown
Source:-1)
 at
org.springframework.util.concurrent.FutureUtils.lambda$toSupplier$0(FutureUtils.java:74)
 at
org.springframework.util.concurrent.FutureUtils$$Lambda/0x000001679aa98cb8.get(Unknown
Source:-1)
 at
java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1768)
 at
java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:-1)
 at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
 at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
 at java.lang.Thread.runWith(Thread.java:1596)
 at java.lang.Thread.run(Thread.java:1583)


Regards,
Fabien ARRAULT

-- 


This e-mail, any attachments and the information contained therein ("this 

message") are confidential and intended solely for the use of the 
addressee(s). 
If you have received this message in error please send it 
back to the sender and 
delete it. Unauthorized publication, use, 
dissemination or disclosure of this 
message, either in whole or in part is 
strictly prohibited.




Ce message électronique et tous les fichiers 
joints ainsi que les informations contenues dans ce message (ci-après "le 
message") sont confidentiels et destinés exclusivement à l'usage de la 
personne à laquelle ils sont adressés. Si vous avez reçu ce message par 
erreur, merci de le renvoyer à son émetteur et de le détruire. Toute 
diffusion, publication, totale ou partielle ou divulgation sous quelque 
forme que ce soit non expressément autorisée de ce message, est interdite.

Reply via email to