Open franz1981 opened 8 months ago
adding @vietj and @jponge because some of these are on both Mutiny and Vertx.
Adding @mkouba because Arc
seems affected the same.
This is the reduced list, ignoring things which belong to OpenJDK instead, and we have not much control of:
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1372)
io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
io.vertx.core.impl.VertxImpl.beginDispatch2(VertxImpl.java:1378)
io.vertx.core.impl.VertxImpl.beginDispatch(VertxImpl.java:1366)
io.vertx.core.impl.ContextInternal.beginDispatch(ContextInternal.java:301)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:43)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:29)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
io.quarkus.arc.impl.InjectionPointProvider.set(InjectionPointProvider.java:33)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:556)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:539)
io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:572)
io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:331)
io.quarkus.arc.impl.ArcContainerImpl$3.get(ArcContainerImpl.java:328)
io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:58)
io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory.createInstance(ArcBeanFactory.java:27)
org.jboss.resteasy.reactive.server.handlers.InstanceHandler.handle(InstanceHandler.java:26)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:139)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContext(SmallRyeThreadContext.java:194)
io.smallrye.context.SmallRyeThreadContext.getCurrentThreadContextOrDefaultContexts(SmallRyeThreadContext.java:160)
io.smallrye.mutiny.context.DefaultContextPropagationInterceptor.getThreadContext(DefaultContextPropagationInterceptor.java:12)
io.smallrye.mutiny.context.BaseContextPropagationInterceptor.decorate(BaseContextPropagationInterceptor.java:33)
io.smallrye.mutiny.infrastructure.Infrastructure.decorate(Infrastructure.java:152)
io.smallrye.mutiny.vertx.AsyncResultUni.<init>(AsyncResultUni.java:21)
io.smallrye.mutiny.vertx.AsyncResultUni.toUni(AsyncResultUni.java:17)
io.vertx.mutiny.sqlclient.Pool.getConnection(Pool.java:115)
io.vertx.mutiny.pgclient.PgPool_Ly1M_jXJtinTvrqFDvbMm7TSXGc_Synthetic_ClientProxy.getConnection(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:53)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
io.smallrye.context.SmallRyeThreadContext.withThreadContext(SmallRyeThreadContext.java:93)
io.smallrye.context.impl.SlowActiveContextState.<init>(SlowActiveContextState.java:31)
io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:34)
io.smallrye.context.impl.SlowCapturedContextState.begin(SlowCapturedContextState.java:13)
io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:20)
io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:130)
io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.set(ThreadLocal.java:253)
io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:133)
io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
com.intellij.rt.debugger.agent.CaptureStorage.capture(CaptureStorage.java:43)
io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java)
io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:290)
io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:205)
io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:667)
io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:678)
io.vertx.core.impl.ContextInternal.setTimer(ContextInternal.java:478)
io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onEnqueue(SqlConnectionPool.java:219)
io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onConnect(SqlConnectionPool.java:235)
io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:593)
io.vertx.core.net.impl.pool.Task.runNextTasks(Task.java:43)
io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:91)
io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository_ClientProxy.listAll(Unknown Source)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource.getAllFortunes(FortuneResource.java:22)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneResource$quarkusrestinvoker$getAllFortunes_17630c6d41f1fd48ab8b75db07f293217742deee.invoke(Unknown Source)
org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
io.quarkus.virtual.threads.ContextPreservingExecutorService$ContextPreservingRunnable.run(ContextPreservingExecutorService.java:45)
java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
/cc @cescoffier (virtual-threads), @ozangunalp (virtual-threads)
Hm, so we use ThreadLocal
in the InjectionPointProvider
and in the DecoratorDelegateProvider
. We would either need to create a new abstraction or piggyback on the CurrentContextFactory
. Which would be a misuse but on the other hand it's a perfect fit :shrug:.
CC @manovotn @Ladicek
The vertx one seems to be triggered by ContextPreservingExecutorService
, perhaps ContextPreservingExecutorService
should check whether it is a vertx thread or not before using vertx api.
@vietj I see that despite starting with ContextPreservingExecutorService
it will end up hitting the combiner too
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
io.netty.util.internal.InternalThreadLocalMap.slowGet(InternalThreadLocalMap.java:130)
io.netty.util.internal.InternalThreadLocalMap.get(InternalThreadLocalMap.java:117)
io.netty.util.concurrent.FastThreadLocal.get(FastThreadLocal.java:136)
io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:85)
io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
your solution at
perhaps ContextPreservingExecutorService should check whether it is a vertx thread or not before using vertx api.
makes sense to me: the good thing is that on virtual threads (and the blocking thread pool as well) we can always await while asking this to be executed on the "right" event loop. I believe is better for both cases, but let's hear @cescoffier because maybe I'm missing something there
Specifically for the ArC part, we don't do anything similar to Jackson. That is, we don't use thread locals as a terrible replacement for object pooling.
@mkouba We could also put these objects into the CreationalContextImpl
. I remember I already showed this to you a long time ago for the DecoratorDelegateProvider
, it was very terribly built, but the principle seems sound to me. I unfortunately don't have the code anymore, but I remember it wasn't too hard to build.
I will try to think about the pool, but I think we should not use the vertx pool like this from a virtual thread too :-)
@vietj yep I think the same: we could just enqueue a task to the right I/O thread (or - better - the one from which the request was coming). IIRC Virtual Threads on Quarkus already have a limited concurrency (using a Semaphore
), so there shouldn't have any problem of backpressure as well.
@franz1981 in pool impl:
public Future<SqlConnection> getConnection() {
ContextInternal current = vertx.getOrCreateContext();
if (pipelined) {
return current.failedFuture("Cannot acquire a connection on a pipelined pool");
}
Promise<SqlConnectionPool.PooledConnection> promise = current.promise();
acquire(current, connectionTimeout, promise);
return promise.future().map(conn -> {
SqlConnectionInternal wrapper = driver.wrapConnection(current, conn.factory(), conn);
conn.init(wrapper);
return wrapper;
});
}
we could check here whether it's vertx thread or not
but TBH we should discuss the use case here, because I am not sure to undrstand :-)
@franz1981 we could perhaps also change CombinerExecutor
to use a volatile Thread
field and later on compare it against the current thread instead of a thread local, piggy backing on the CombinerExecutor state itself
@vietj
IDK, https://github.com/eclipse-vertx/vert.x/blob/adbe97600cc6215f15ce2fac629c89f93618ca8f/src/main/java/io/vertx/core/net/impl/pool/CombinerExecutor.java#L85 is already pretty complex. To me, issuing an asynchronous call from a virtual thread, which has been designed to wait, is not a good use of it, especially if the upper mutiny layers will wait for that acquire to happen - whcih means that the pooled connection acquisition is key to make progress there.
the combiner is designed to be usable from any thread, it's not tied to netty, it just needs a thread to operate, removing the thread local here will not make the code more complex I think.
so perhaps it should be done before entering the pool and that is the code to question I think.
We cannot, because of reentrant actions: if we don't use a ConcurrentHashMap<Thread..., > we have no way to bring the context of where we are, around. And CHM is super slow for the other cases. I would focus instead into the fact that doesn't look a legit use case: if we allow to have 100000 threads we cannot have a single structure which hold all of them, really....is not safe. The scale of the problems we have now is not the same as the Jboss blocking thread pool
@mkouba We could also put these objects into the
CreationalContextImpl
. I remember I already showed this to you a long time ago for theDecoratorDelegateProvider
, it was very terribly built, but the principle seems sound to me. I unfortunately don't have the code anymore, but I remember it wasn't too hard to build.
@Ladicek Well, things related to CreationalContext
are usually not easy and straightforward but I agree that it would be a perfect fit for this kind of information. Feel free to add this issue to your TODO list then ;-).
We cannot, because of reentrant actions: if we don't use a ConcurrentHashMap<Thread..., > we have no way to bring the context of where we are, around. And CHM is super slow for the other cases. I would focus instead into the fact that doesn't look a legit use case: if we allow to have 100000 threads we cannot have a single structure which hold all of them, really....is not safe. The scale of the problems we have now is not the same as the Jboss blocking thread pool
we need to understand when this pool is not accessed from event loop and the use cases
we need to understand when this pool is not accessed from event loop and the use cases
The stack trace is
VirtualThread[#172,quarkus-virtual-thread-0]/runnable@ForkJoinPool-1-worker-1
java.base/java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:236)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:194)
java.base/java.lang.ThreadLocal.get(ThreadLocal.java:172)
com.intellij.rt.debugger.agent.CaptureStorage.capture(CaptureStorage.java:43)
io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java)
io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:817)
io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:290)
io.netty.util.concurrent.AbstractScheduledEventExecutor.schedule(AbstractScheduledEventExecutor.java:205)
io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:667)
io.vertx.core.impl.VertxImpl.scheduleTimeout(VertxImpl.java:678)
io.vertx.core.impl.ContextInternal.setTimer(ContextInternal.java:478)
io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onEnqueue(SqlConnectionPool.java:219)
io.vertx.sqlclient.impl.pool.SqlConnectionPool$1PoolRequest.onConnect(SqlConnectionPool.java:235)
io.vertx.core.net.impl.pool.SimpleConnectionPool$Acquire$3.run(SimpleConnectionPool.java:593)
io.vertx.core.net.impl.pool.Task.runNextTasks(Task.java:43)
io.vertx.core.net.impl.pool.CombinerExecutor.submit(CombinerExecutor.java:91)
io.vertx.core.net.impl.pool.SimpleConnectionPool.execute(SimpleConnectionPool.java:244)
io.vertx.core.net.impl.pool.SimpleConnectionPool.acquire(SimpleConnectionPool.java:639)
io.vertx.sqlclient.impl.pool.SqlConnectionPool.acquire(SqlConnectionPool.java:239)
io.vertx.sqlclient.impl.PoolImpl.acquire(PoolImpl.java:176)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:139)
io.vertx.sqlclient.impl.PoolImpl.getConnection(PoolImpl.java:126)
io.vertx.sqlclient.impl.PoolBase.getConnection(PoolBase.java:56)
io.vertx.pgclient.PgPool_7weXgYHcaRck_k2l7dGKfXZMdD8_Synthetic_ClientProxy.getConnection(Unknown Source)
io.vertx.mutiny.sqlclient.Pool.lambda$getConnection$11(Pool.java:116)
io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
io.smallrye.mutiny.vertx.AsyncResultUni.subscribe(AsyncResultUni.java:31)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:60)
io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:65)
io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:46)
me.escoffier.fortune.virtual.reactive.postgresql.FortuneRepository.listAll(FortuneRepository.java:62)
which is triggered by https://github.com/franz1981/fortune-benchmark/blob/fix_new_quarkus/fortune-virtual-thread-postgresql/src/main/java/me/escoffier/fortune/virtual/reactive/postgresql/FortuneRepository.java#L51-L62
public List<Fortune> listAll() {
List<Fortune> list = new ArrayList<>();
return datasource.getConnection().flatMap(connection ->
connection.query(Statements.SELECT_ALL_FORTUNES).execute()
.map(rs -> {
for (Row row : rs) {
list.add(new Fortune(row.getInteger(0), row.getString(1)));
}
return list;
})
.onTermination().call(connection::close)
).await().indefinitely();
}
where datasource.getConnection
will end up calling the vertx pool
@FroMage I've taken a quick look at the Virtual Thread stacks and the ThreadLocal usage reported at https://github.com/smallrye/smallrye-context-propagation/issues/424 is particularly bad here as well (mostly because ThreadLocal(s)
are bad with virtual threads
Adding @mariofusco here as well, which was working on that issue IIRC
In short, not just the remove (which is not nice), but the ThreadLocal
itself is not good here
I'm all for removing ThreadLocal
uses, but any new strategy tested needs benchmarking validation 🤷
That's not a problem, I can help with that one @FroMage - or we can have (given that virtual thread(s) are not the most common usage yet) the option of using an hybrid data-structure ie using a ConcurrentHashMap<Thread, Whatever> to emulate a thread-local storage for VT and still using ThreadLocal(s) for the rest of the world. Clearly it requires periodically to verify if all the thread(s) are still alive, because no concurrent weak ref map exists in java AFAIK. Or - we can think to use https://openjdk.org/jeps/464 for virtual thread(s) only: I'm opened to all the possibilities here, but probably worth opening the issue on the ctx prog side, wdyt?
Perfect topic to discuss at the f2f, much easier in person, unless this is urgent?
+100 for F2F discussion on this, great idea (almost forgot we have the F2F :D )
@Ladicek @mkouba I can just let this issue opened or you want to track your ones separately?
@franz1981 I submitted a PR for ArC (#39837), so feel free to do whatever you wish with this issue.
Many thanks @Ladicek let me know if I need to take a look there for some reason, and I will try (likely in the next days)
Actually I might have been a bit too fast, I'll let you know :-) From performance perspective, I'm adding a field (one at the moment, but may expand to two) into CreationalContextImpl
, which is used super often, so it might be good to check. But let me test first, if that even makes sense.
Actually I might have been a bit too fast, I'll let you know :-) From performance perspective, I'm adding a field (one at the moment, but may expand to two) into
CreationalContextImpl
, which is used super often, so it might be good to check. But let me test first, if that even makes sense.
That field is not initialized in most cases, or?
That is correct, but it makes all the CreationalContextImpl
objects bigger, which might be a concern.
That is correct, but it makes all the
CreationalContextImpl
objects bigger, which might be a concern.
Hm, "bigger" in the sense that you'll need roughly 4 more bytes for each instance? I don't think you'll be able to spot the difference (except for microbenchmarks where you create millions of beans ofc ;-).
Yeah, it is 4 bytes for each CC. It isn't much, but indeed, it is 4 more bytes for each created bean. Probably not too big of a deal.
I ended up making that 2 fields for the CC, so it's 8 bytes more for each created bean. Otherwise, #39837 seems OK, so @franz1981 if you think that PR needs some care, please take a look. Thanks!
What's the state of this on your side @Ladicek @mkouba ?
and @vietj ?
What's the state of this on your side @Ladicek @mkouba ?
Ladislav's PR was merged so ArC should not use thread locals at all...
Similarly to https://github.com/FasterXML/jackson-core/issues/919
in quarkus we have few parts which are still using thread locals
eg running the test https://github.com/franz1981/fortune-benchmark/blob/main/fortune-virtual-thread-postgresql/src/test/java/me/escoffier/fortune/virtual/reactive/postgresql/test/FortuneApiTest.java adding the parameter
-Djdk.traceVirtualThreadLocals=true
should report this stack trace (which ca be used in IDEA stack trace analyzer to fix the issues, one by one):
I think JDK 21 have a BUG on the mentioned property, because the stacks which contains
are absolutely fine. This is a sign that most users didn't used this feature to make sure are not using thread locals on virtual thread(s) OR that loom is not that used (yet, which makes sense) OR that thread local(s) is not widely used (which is unlikely, really).
Instead, https://github.com/openjdk/jdk/blob/b9c76dedf4aa2248a5e561a535c9e3e181f7836a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java#L2799 shows that OpenJDK still have to fix its own
ThreadLocal
usages as well, although, is not a big deal per-se, given that the number of class-loading operations should not keep on increasing during a run, in theory.