You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, we have an @MicronautTest failure when we migrate to micronaut 4 (in parent pom, io.micronaut:micronaut-platform and io.micronaut:micronaut-core-bom versions are 4.3.5):
The test is negative test. In controller it would throw an exception, and in io.micronaut.http.server.exceptions.ExceptionHandler extended handler it calls io.micronaut.http.context.ServerRequestContext.currentRequest() and got an empty return.
In the test, before the controller throws the exception, ServerRequestContext.currentRequest() would return the NettyHttpRequest object; however in the ExceptionHandler.handle() method, calling ServerRequestContext.currentRequest() would return empty optional.
After some debugging, we found the following two classes related to retrieve current request
io.netty.util.concurrent.FastThreadLocal:
public final V get() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(this.index);
return v != InternalThreadLocalMap.UNSET ? v : this.initialize(threadLocalMap);
}
and
io.netty.util.internal.InternalThreadLocalMap
that has
private Object[] indexedVariables = newIndexedVariableTable()
Normally indexedVariables[1] (index=1)contains the current request object (wrapped) in this test context.
When this bug appears, indexedVariables[1] would be InternalThreadLocalMap.UNSET.
The window that indexedVariables[1] is set to be InternalThreadLocalMap.UNSET, is after the Controller raised Exception and before
PlatformExceptionMapper.handle(HttpRequest request, PlatformException exception)
The stack that set indexedVariables[1] is set to be InternalThreadLocalMap.UNSET is:
removeIndexedVariable:371, InternalThreadLocalMap (io.netty.util.internal)
remove:255, FastThreadLocal (io.netty.util.concurrent)
remove:241, FastThreadLocal (io.netty.util.concurrent)
remove:58, ThreadContext (io.micronaut.core.propagation)
close:-1, PropagatedContextImpl$$Lambda/0x0000007001c0ce08 (io.micronaut.core.propagation)
executeRouteAndConvertBody:496, RouteExecutor (io.micronaut.http.server)
lambda$callRoute$6:465, RouteExecutor (io.micronaut.http.server)
get:-1, RouteExecutor$$Lambda/0x0000007001fb5908 (io.micronaut.http.server)
lambda$async$1:87, ExecutionFlow (io.micronaut.core.execution)
run:-1, ExecutionFlow$$Lambda/0x0000007001fb8630 (io.micronaut.core.execution)
record:141, CompositeTimer (io.micrometer.core.instrument.composite)
lambda$wrap$0:193, Timer (io.micrometer.core.instrument)
run:-1, Timer$$Lambda/0x0000007001c0bac8 (io.micrometer.core.instrument)
record:141, CompositeTimer (io.micrometer.core.instrument.composite)
lambda$wrap$0:193, Timer (io.micrometer.core.instrument)
run:-1, Timer$$Lambda/0x0000007001c0bac8 (io.micrometer.core.instrument)
run:314, ThreadPerTaskExecutor$TaskRunner (java.util.concurrent)
runWith:1596, Thread (java.lang)
run:309, VirtualThread (java.lang)
run:190, VirtualThread$VThreadContinuation$1 (java.lang)
enter0:320, Continuation (jdk.internal.vm)
enter:312, Continuation (jdk.internal.vm)
I noticed io.micronaut.core.propagation.ThreadContext.remove(58) in the above stack. When the bug doesn't appear, during above window, ThreadContext.remove is not called.
I like to understand what would the condition that triggers micronaut to invoke io.micronaut.core.propagation.ThreadContext.remove()?
If micronaut 4 microservice is deployed, would ThreadContext.remove be invoked? If yes, when?
Can we always assume that we can access the current request when an exception mapper is invoked?
The described bug has a complex condition to appear. We have a @MicronautTest that has many @test methods. The problematic test needs to run after a dozen of other tests (that are all client controller tests without go across the wire) in order to show the bug. If I change the test order, then the bug is not going to appear.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi, we have an @MicronautTest failure when we migrate to micronaut 4 (in parent pom, io.micronaut:micronaut-platform and io.micronaut:micronaut-core-bom versions are 4.3.5):
The test is negative test. In controller it would throw an exception, and in io.micronaut.http.server.exceptions.ExceptionHandler extended handler it calls io.micronaut.http.context.ServerRequestContext.currentRequest() and got an empty return.
In the test, before the controller throws the exception, ServerRequestContext.currentRequest() would return the NettyHttpRequest object; however in the ExceptionHandler.handle() method, calling ServerRequestContext.currentRequest() would return empty optional.
After some debugging, we found the following two classes related to retrieve current request
io.netty.util.concurrent.FastThreadLocal:
public final V get() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(this.index);
return v != InternalThreadLocalMap.UNSET ? v : this.initialize(threadLocalMap);
}
and
io.netty.util.internal.InternalThreadLocalMap
that has
private Object[] indexedVariables = newIndexedVariableTable()
Normally indexedVariables[1] (index=1)contains the current request object (wrapped) in this test context.
When this bug appears, indexedVariables[1] would be InternalThreadLocalMap.UNSET.
The window that indexedVariables[1] is set to be InternalThreadLocalMap.UNSET, is after the Controller raised Exception and before
PlatformExceptionMapper.handle(HttpRequest request, PlatformException exception)
The stack that set indexedVariables[1] is set to be InternalThreadLocalMap.UNSET is:
removeIndexedVariable:371, InternalThreadLocalMap (io.netty.util.internal)
remove:255, FastThreadLocal (io.netty.util.concurrent)
remove:241, FastThreadLocal (io.netty.util.concurrent)
remove:58, ThreadContext (io.micronaut.core.propagation)
close:-1, PropagatedContextImpl$$Lambda/0x0000007001c0ce08 (io.micronaut.core.propagation)
executeRouteAndConvertBody:496, RouteExecutor (io.micronaut.http.server)
lambda$callRoute$6:465, RouteExecutor (io.micronaut.http.server)
get:-1, RouteExecutor$$Lambda/0x0000007001fb5908 (io.micronaut.http.server)
lambda$async$1:87, ExecutionFlow (io.micronaut.core.execution)
run:-1, ExecutionFlow$$Lambda/0x0000007001fb8630 (io.micronaut.core.execution)
record:141, CompositeTimer (io.micrometer.core.instrument.composite)
lambda$wrap$0:193, Timer (io.micrometer.core.instrument)
run:-1, Timer$$Lambda/0x0000007001c0bac8 (io.micrometer.core.instrument)
record:141, CompositeTimer (io.micrometer.core.instrument.composite)
lambda$wrap$0:193, Timer (io.micrometer.core.instrument)
run:-1, Timer$$Lambda/0x0000007001c0bac8 (io.micrometer.core.instrument)
run:314, ThreadPerTaskExecutor$TaskRunner (java.util.concurrent)
runWith:1596, Thread (java.lang)
run:309, VirtualThread (java.lang)
run:190, VirtualThread$VThreadContinuation$1 (java.lang)
enter0:320, Continuation (jdk.internal.vm)
enter:312, Continuation (jdk.internal.vm)
I noticed io.micronaut.core.propagation.ThreadContext.remove(58) in the above stack. When the bug doesn't appear, during above window, ThreadContext.remove is not called.
I like to understand what would the condition that triggers micronaut to invoke io.micronaut.core.propagation.ThreadContext.remove()?
If micronaut 4 microservice is deployed, would ThreadContext.remove be invoked? If yes, when?
Can we always assume that we can access the current request when an exception mapper is invoked?
The described bug has a complex condition to appear. We have a @MicronautTest that has many @test methods. The problematic test needs to run after a dozen of other tests (that are all client controller tests without go across the wire) in order to show the bug. If I change the test order, then the bug is not going to appear.
Beta Was this translation helpful? Give feedback.
All reactions