Open stepanv opened 1 year ago
a full reproducer would be good. Thanks
ok, I'll prepare some in next hours, thx @graemerocher for such prompt response!
@graemerocher I added 2 PRs:
3.8.x
where the test is indeed failing: https://github.com/micronaut-projects/micronaut-core/pull/83434.0.x
it seems to be running ok: https://github.com/micronaut-projects/micronaut-core/pull/8344So now the obvious question:
Thx!
I do confirm that when using Flux.deferContextual()
and Mono.deferContextual()
to access the HttpRequest and its attributes, nothing gets wrong in my reproducer even with Micronaut 3.8.
So the question is what to do with @RequestScope
on Micronaut 3.x which is using unsupported (buggy) ServerRequestContext.currentRequest()
?
I would say we should improve the documentation state that @RequestScope
can only be used when using imperative / blocking code since its reliance on threadlocals mean it will never work well with reactive. It may "work" by chance in 4.0.x but I have no doubt there is another combination of reactive operators that will break it since reactive is fundamentally incompatible with thread local usage.
By any chance, isn't it possible to use Reactor's Hooks to always align the ThreadLocal value with what is in the Reactor's context? This way the injection framework should be able to pick up correct instance from the ThreadLocal. Just brainstorming...
@graemerocher I found out your commit https://github.com/micronaut-projects/micronaut-core/commit/79c56c65b40ca361904f2a723041379b15c7f26f on 4.0.x
where you removed the ReactorInstrumentation
actually fixes the problem. I'm unable to run the tests locally so I'm not sure if something else gets broken. See my PR https://github.com/micronaut-projects/micronaut-core/pull/8413 for branch 3.7.x
.
Anyway, it would be great if Micronaut was capable (or at least tried) of maintaining the correct state of the ThreadLocal
s. So far it worked quite well. Thanks to that the application code is much cleaner and much better testable. Additionally there are things that are impossible to achieve without ThreadLocals (e.g., logging with MDC contextual info).
nevermind, without the ReactorInstrumentation
many other things stop working
TL simply cannot work well in pooled, asynchronous situations (like reactive). It's a losing battle. It will be better with virtual threads.
In the same time, you guys were giving a lot of effort to make Micronaut's TL based RequestScope work even in the reactive world. Frankly, this is one of the reasons to use Micronaut. It is hacky and possibly not working under every scenario, but it's worth the effort. Look at vmware, they hacked the x86 arch to make virtualization work and it's a billion $ worth company.
Not sure what is the plan for the Micronaut 4.0.x but we need this feature to be working in 3.x. Our production is intermittently failing when under a load.
To mitigate the problem, I found out my reproducer gets fixed when the TL is set through the Reactor Context. It even seems like you guys intended that with the ReactiveInvocationInstrumenterFactory
but didn't finish it. Please take a look at https://github.com/micronaut-projects/micronaut-core/pull/8421 PR and let me know what you think.
cc @graemerocher , @yawkat , @dstepanov
Hi team,
we stumbled on an intermittent bug that
ServerRequestContext
may get corrupted. By corrupted I mean that it is still available but not in the expected state (e.g., the object I store there is later found missing).It happens only under given conditions (see also a reproducer bellow):
@Controller
has a method returning aMono<HttpResponse<Flux<?>>>
Mono
Flux
Why do we do the 2 layer reactive types controller?
Mono
to reactively decide (because it depends on a result from another backend) whether we want to return200
and continue processing the request OR to return4xy
error.200
we still need to reactively return a series of objects, some get returned sooner other later (e.g., after another backend call)Steps to Reproduce
Following
@MicronautTest
annotated test demonstrates the issue:Expected Behaviour
The tests should all suceed, especially the parallel ones (see the
parallel()
test with higher number denoting the number of parallel executions).Actual Behaviour
For higher volume of parallel requests, Micronaut's
ServerRequestContext
(which is what@RequestScope
is founded on) may get corrupted. In the above described reproducer, the corruption is detected when a set field of a@RequestScope
bean is later found as stillnull
. The same problem can be demonstrated by setting an attributeServerRequestContext.currentRequest().get().setAttribute()
which is later intermittently found as not present.Environment Information
Darwin Kernel Version 22.1.0
3.5.1
openjdk 17.0.3 2022-04-19 LTS
Example Application
If needed, I can create a test in groovy+spock in
micronaut-core
codebase as a full reproducer.