Hello Fabien,

Good point, I refactored the platform-http component because it was
behaving differently compared to an async Spring Boot rest service. In
particular, before that commit, the component was not using the Spring Boot
async thread pool (like async spring boot does) but the sync thread was
being used, therefore, before that commit the component was not async at
all, and in some cases (under some load) the server stopped working.

With the commit you mentioned, I wanted the Camel Http Platform to be as
close as possible to an Async Spring Boot service from a threading
perspective and, of course, avoid the locks.
I didn't notice the creation of multiple tasks, thanks for reporting it,
I'll have a look for sure, by any chance do you have any suggestions on how
to solve this issue? IIRC there are tests for the issue we faced with the
lock, and some assertions regarding the asynchronicity.

Regards,
Federico

Il giorno ven 24 gen 2025 alle ore 15:50 Fabien Arrault <
farra...@consulting-for.d-edge.com> ha scritto:

> 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