Comments (20)
Btw, the tool I used is https://github.com/astrofrog/psrecord.
from spring-boot.
Yeah, we shouldn't fiddle with the Undertow executors.
The default one produces this plot:
As soon as we meddle with it, it starts breaking.
With virtual threads:
With virtual threads and concurrency throttle to 200:
With platform threads and concurrency throttle to 200:
It breaks in all configurations when setting the executor.
I'm going to revert the virtual thread support for Undertow.
from spring-boot.
Undertow 2.3.10.Final, SimpleAsyncTaskExecutor
with 200 concurrency limit and virtual threads enabled:
It's better, but there's still a problem somewhere.
from spring-boot.
This is what 2.3.10 with the default executor looks like:
from spring-boot.
That's one of the drawbacks when moving from a thread pool to virtual threads per task without a pool. When using thread pools, they usually have an upper bound for the size, limiting the maximum resource consumption. Unpooled virtual threads don't have that. I assume the application you're benchmarking is a simple demo application which just returns some hardcoded data? Because usually, if there's no pool in the webserver, some other pool in the application is limiting the throughput (e.g. the connection pool to the database).
Btw, you don't see the memory usage in VisualVM, as this is direct allocated memory, which is off-heap.
from spring-boot.
Yes, it's the Spring Boot demo project with a basic GET endpoint with no return value. Sorry, could you explain the cause for this again? Aren't virtual threads still limited by the size of the platform threads in the carrier pool? The Tomcat and Jetty servlets don't seem to have the same issue when using virtual threads. Also, thank you for the explanation about the profiler.
from spring-boot.
One possibility could be this:
1. VT1: Start accepting the request
2. VT1: Allocate memory
3. VT1: Block on something (maybe reading http from the TCP socket)
4. VT1: Handle request
5. VT1: Free memory
With enough requests lined up, the block at 3. could be the reason why the memory is exhausted. At this block, the scheduler frees the underlying platform thread, and switches to a new virtual thread, which allocates memory, blocks, a new virtual thread is scheduled, allocates memory, etc etc.
from spring-boot.
You could use a SimpleAsyncTaskExecutor
with setConcurrencyLimit
to limit the maximum allowed number of virtual threads.
from spring-boot.
as long as I remember, there is an issue that talks about virtual threads in Spring Boot here:
#38819
there is one part I saw that says in version 3.3.x the usage of the virtual thread must automatically happen for undertow. so can it be related to this issue?
can u check if this also happens with version 3.2.x ? @Tythor
from spring-boot.
@MetaiR
Yes, I can confirm the same behavior happens in v3.2.3. Since spring.threads.virtual.enabled=true
does not apply to Undertow servlets until v3.3.0, I had to use the undertowDeploymentInfoCustomizer
to enable virtual threading.
@mhalbritter
I attempted to use a SimpleAsyncTaskExecutor
with virtual threads with a concurrency limit of 10, but the issue still persisted.
@Bean
public UndertowDeploymentInfoCustomizer undertowDeploymentInfoCustomizer() {
return deploymentInfo -> {
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
simpleAsyncTaskExecutor.setVirtualThreads(true);
simpleAsyncTaskExecutor.setConcurrencyLimit(10);
deploymentInfo.setExecutor(simpleAsyncTaskExecutor);
};
}
However, this led me to try using a non virtual threaded executor instead. Surprisingly, I observed the same behavior, albeit at a much slower growth rate of about 2m for 20GB instead of 30s. These three executors produced similar results:
deploymentInfo.setExecutor(new SimpleAsyncTaskExecutor());
deploymentInfo.setExecutor(Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory()));
deploymentInfo.setExecutor(Executors.newThreadPerTaskExecutor(Executors.defaultThreadFactory()));
However, when using
deploymentInfo.setExecutor(Executors.newCachedThreadPool());
the memory growth disappeared. Looking into the implementation, the main difference is that the cachedThreadPool executor has a keepAliveTime
of 60s, while the others have a keepAliveTime
of 0s.
Reducing the keepAliveTime
to as low as 1s did not cause the memory growth in my tests. But setting it to 0s reproduced the same issue.
deploymentInfo.setExecutor(new ThreadPoolExecutor(0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS, new SynchronousQueue<>()));
So it looks like this issue is not unique to virtual threads, but perhaps with the way Undertow is handling executors?
from spring-boot.
Yeah, looks like something is wrong here, but it doesn't seem to be in Spring Boot. Please open an issue on the Undertow tracker, and feel free to drop the link in this issue. Thanks!
from spring-boot.
We decided to reopen the issue. The out of box experience with virtual threads in undertow is bad, and if we (or the undertow team) can't fix the problems with it, we might remove virtual threads support for undertow again.
from spring-boot.
Okay, I was about to suggest disabling the auto configuration for spring.threads.virtual.enabled=true
in v3.3.x. This issue would likely be a problem that most users will not be aware of. Thanks!
cff1b33
from spring-boot.
It looks like Undertow may have a memory leak. @Tythor, can you please retry your scenario with Undertow downgraded to 2.3.10.Final and see if it helps?
from spring-boot.
Hi @wilkinsona, downgrading Undertow to 2.3.10.Final on Spring Boot v3.3.0-M2 did significantly slow the memory growth. However, I am still seeing suspicious behavior when providing an executor in the UndertowDeploymentInfoCustomizer
. Although I'm not sure if it's due to a memory leak or just inefficient memory consumption within Undertow. Perhaps @mhalbritter may be able to diagnose the issue better with his plots. I also tried using 2.3.11.Final and encountered the same issue as the one in 2.3.12.Final.
from spring-boot.
Thank you @mhalbritter, these plots are very similar to the results I observed as well.
from spring-boot.
Hello,
I am using Spring Boot 3.2.4 and Undertow with OpenJDK by Corretto, I don't have this issue.
My application is an API REST, it is running by several days.
from spring-boot.
@cassiusvm How have you configured Undertow to use virtual threads?
from spring-boot.
@wilkinsona Right, using the @configuration tip in a Docker image jelastic maven 3.9.5 OpenJDK 21.
My bad, I said Correto.
from spring-boot.
I realise its not a spring issue per-se, but hoping that this is useful.
When configuring with
Xnio.getInstance().setExternalExecutorService(Executors.newVirtualThreadPerTaskExecutor())
The service will quickly blow up.. I think due to use of ThreadLocal buffer strategy.
In DefaultByteBufferPool
, the following line might cause a problem with virtual threads:
ThreadLocalData get() {
return localsByThread.get(Thread.currentThread());
}
It is possible to make it used unpooled buffers, with
io.undertow.Undertow.builder().setByteBufferPool(XnioByteBufferPool(Pool.DIRECT))
but the performance of this is worse (by 50%) than the default, so no point.
Anyhow, hope thats of some use if somebody is looking into this.
from spring-boot.
Related Issues (20)
- spring-boot-dependencies cannot be used with repositories that ban com.oracle.database.jdbc:ojdbc-bom
- spring-boot-dependencies cannot be used with repositories that ban com.oracle.database.jdbc:ojdbc-bom
- Upgrade to Asciidoctor Gradle Plugin 4.0.2
- Avoid calling getProject() during execution of StarterMetadata task
- Avoid calling getProject() during execution of StarterMetadata task
- Avoid calling getProject() during execution of StarterMetadata task
- Avoid calling getProject() during execution of AutoConfigurationMetadata task
- Avoid calling getProject() during execution of AutoConfigurationMetadata task
- Avoid calling getProject() during execution of AutoConfigurationMetadata task
- Avoid calling getProject() during execution of publishMavenPublicationToProjectRepository
- Avoid calling getProject() during execution of publishMavenPublicationToProjectRepository
- Avoid calling getProject() during execution of publishMavenPublicationToProjectRepository
- Avoid calling getProject() when checking configuration metadata
- Avoid calling getProject() when checking configuration metadata
- Avoid calling getProject() when checking configuration metadata
- IllegalArgumentException can be thrown when running an uber jar on a shared drive HOT 3
- Avoid calling getProject() during execution of extractVersionConstraints
- Avoid calling getProject() during execution of extractVersionConstraints
- Using prometheus 0.x with distribution percentiles lead to "Counts in ClassicHistogramBuckets cannot be negative."
- LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from spring-boot.