Open highmtworks opened 3 years ago
As the nested calls of computeIfAbsent
are invoked on the same thread (as the stack trace shows),
I suspect that adding synchronized
( without removing computedIfAbsent
) will have no effect for this issue.
I am sorry to be late, but I created a possible reproducer. Could you try this one please ?
mn create-app test5469
add the following Test5469Controller.java
file in test5469/src/main/java/test5469
package test5469;
import javax.inject.Inject;
import io.micronaut.context.ApplicationContext;
import io.micronaut.context.annotation.Factory;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.runtime.http.scope.RequestScope;
@Controller("/test5469")
public class Test5469Controller {
@Inject
private ApplicationContext applicationContext;
@Get(produces = MediaType.TEXT_PLAIN)
public String index() {
var c = applicationContext.getBean(Component1.class);
c.touch();
return "OK";
}
}
@Factory class Component1Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component1 getComponent() { var c = applicationContext.getBean(Component2.class); c.touch(); return new Component1(); } }
@Factory class Component2Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component2 getComponent() { var c = applicationContext.getBean(Component3.class); c.touch(); return new Component2(); } }
@Factory class Component3Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component3 getComponent() { var c = applicationContext.getBean(Component4.class); c.touch(); return new Component3(); } }
@Factory class Component4Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component4 getComponent() { var c = applicationContext.getBean(Component5.class); c.touch(); return new Component4(); } }
@Factory class Component5Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component5 getComponent() { var c = applicationContext.getBean(Component6.class); c.touch(); return new Component5(); } }
@Factory class Component6Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component6 getComponent() { var c = applicationContext.getBean(Component7.class); c.touch(); return new Component6(); } }
@Factory class Component7Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component7 getComponent() { var c = applicationContext.getBean(Component8.class); c.touch(); return new Component7(); } }
@Factory class Component8Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component8 getComponent() { var c = applicationContext.getBean(Component9.class); c.touch(); return new Component8(); } }
@Factory class Component9Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component9 getComponent() { var c = applicationContext.getBean(Component10.class); c.touch(); return new Component9(); } }
@Factory class Component10Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component10 getComponent() { var c = applicationContext.getBean(Component11.class); c.touch(); return new Component10(); } }
@Factory class Component11Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component11 getComponent() { var c = applicationContext.getBean(Component12.class); c.touch(); return new Component11(); } }
@Factory class Component12Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component12 getComponent() { var c = applicationContext.getBean(Component13.class); c.touch(); return new Component12(); } }
@Factory class Component13Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component13 getComponent() { var c = applicationContext.getBean(Component14.class); c.touch(); return new Component13(); } }
@Factory class Component14Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component14 getComponent() { var c = applicationContext.getBean(Component15.class); c.touch(); return new Component14(); } }
@Factory class Component15Factory { @Inject private ApplicationContext applicationContext; @RequestScope public Component15 getComponent() { var c = applicationContext.getBean(Component16.class); c.touch(); return new Component15(); } }
@Factory class Component16Factory { @RequestScope public Component16 getComponent() { return new Component16(); } }
class Component1 { public void touch() { } }
class Component2 { public void touch() { } }
class Component3 { public void touch() { } }
class Component4 { public void touch() { } }
class Component5 { public void touch() { } }
class Component6 { public void touch() { } }
class Component7 { public void touch() { } }
class Component8 { public void touch() { } }
class Component9 { public void touch() { } }
class Component10 { public void touch() { } }
class Component11 { public void touch() { } }
class Component12 { public void touch() { } }
class Component13 { public void touch() { } }
class Component14 { public void touch() { } }
class Component15 { public void touch() { } }
class Component16 { public void touch() { } }
gradlew run
then access curl http://localhost:8080/test5469
It will return
{"message":"Internal Server Error: Error instantiating bean of type [test5469.Component1]: Recursive update","_links":{"self":{"href":"/test5469","templated":false}}}
on micronaut 2.5.9.
I tried running the above reproducer for #5810.
Replacing RequestCustomScope
in the reproducer with #5810
https://github.com/micronaut-projects/micronaut-core/blob/5a4c0c30381103e6bf780f3c3930893bfc8fe01a/runtime/src/main/java/io/micronaut/runtime/http/scope/RequestCustomScope.java
still causes "Recursive update" in my environment.
Caused by: java.lang.IllegalStateException: Recursive update
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1763)
at io.micronaut.runtime.http.scope.RequestCustomScope.get(RequestCustomScope.java:85)
at io.micronaut.context.DefaultBeanContext.getScopedBeanForDefinition(DefaultBeanContext.java:2487)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:2427)
at io.micronaut.context.DefaultBeanContext.getProxyTargetBean(DefaultBeanContext.java:1089)
at test5469.$Component3Factory$GetComponent0Definition$Intercepted.$resolveTarget(Unknown Source)
at test5469.$Component3Factory$GetComponent0Definition$Intercepted.interceptedTarget(Unknown Source)
at test5469.$Component3Factory$GetComponent0Definition$Intercepted.touch(Unknown Source)
at test5469.Component2Factory.getComponent(Test5469Controller.java:26)
at test5469.$Component2Factory$GetComponent0Definition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1979)
... 102 common frames omitted
I found this issue when searching for "recursive update" problem. After reading description it seems that it might match my problem. I have created a github project on which this should be reproducible (non-deterministically). It is this one: https://github.com/mancze/micronaut-recursive-update-issue . I'm not sure if it has common cause as @Factory
is not being used in my project.
I'm not sure if it will be useful since @highmtworks already provided reproducer, but since I already prepared it I thought it might be worth mentioning.
@graemerocher It looks similar to https://github.com/micronaut-projects/micronaut-core/pull/3632.
I've been facing the same issue in 2.5.5. I have another service running 2.0.3 with no issues. I have a declarative client (which is singleton by default, right?) that calls a service annotated with @RequestScope
.
I cannot reproduce the issue if I change the service to @Singleton
.
Can I annotate a declarative client @Client
with @RequestScope
?
I am getting the same with the GraphQL library.
It is not reproducable but when it occurs I have to restart the service multiple times until it then just starts working.
Here is my code:
@Slf4j
@Factory
public class GraphQLFactory {
@Bean
public GraphQL graphQL(GraphQLSchemaBuilder schemaBuilder,
List<Instrumentation> instrumentations) {
return GraphQL.newGraphQL(schemaBuilder.getSchema()).instrumentation(new ChainedInstrumentation(instrumentations)).build();
}
@Bean
@Replaces(GraphQLInvocation.class)
@Refreshable
public GraphQLInvocation graphQLInvocation(GraphQL graphQL,
GraphQLExecutionInputCustomizer graphQLExecutionInputCustomizer,
@Nullable BeanProvider<DataLoaderRegistry> dataLoaderRegistry) {
return new DefaultGraphQLInvocation(graphQL, graphQLExecutionInputCustomizer, dataLoaderRegistry);
}
}
And the stacktrace when it occurs:
11:00:39.437 ERROR- Unexpected error occurred: Error instantiating bean of type [io.micronaut.configuration.graphql.GraphQLInvocation]
Message: Recursive update
Path Taken: new GraphQLController(GraphQLInvocation graphQLInvocation,GraphQLExecutionResultHandler graphQLExecutionResultHandler,GraphQLJsonSerializer graphQLJsonSerializer) --> new GraphQLController([GraphQLInvocation graphQLInvocation],GraphQLExecutionResultHandler graphQLExecutionResultHandler,GraphQLJsonSerializer graphQLJsonSerializer) --> GraphQLFactory.graphQLInvocation([GraphQL graphQL],GraphQLExecutionInputCustomizer graphQLExecutionInputCustomizer,BeanProvider dataLoaderRegistry)
io.micronaut.context.exceptions.BeanInstantiationException: Error instantiating bean of type [io.micronaut.configuration.graphql.GraphQLInvocation]
Message: Recursive update
Path Taken: new GraphQLController(GraphQLInvocation graphQLInvocation,GraphQLExecutionResultHandler graphQLExecutionResultHandler,GraphQLJsonSerializer graphQLJsonSerializer) --> new GraphQLController([GraphQLInvocation graphQLInvocation],GraphQLExecutionResultHandler graphQLExecutionResultHandler,GraphQLJsonSerializer graphQLJsonSerializer) --> GraphQLFactory.graphQLInvocation([GraphQL graphQL],GraphQLExecutionInputCustomizer graphQLExecutionInputCustomizer,BeanProvider dataLoaderRegistry)
at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2324)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2279)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2291)
at io.micronaut.context.DefaultBeanContext.createRegistration(DefaultBeanContext.java:3054)
at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2944)
at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2730)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1729)
at io.micronaut.context.AbstractBeanResolutionContext.getBean(AbstractBeanResolutionContext.java:89)
at io.micronaut.context.AbstractInitializableBeanDefinition.resolveBean(AbstractInitializableBeanDefinition.java:2165)
at io.micronaut.context.AbstractInitializableBeanDefinition.getBeanForConstructorArgument(AbstractInitializableBeanDefinition.java:1328)
at com.devsoap.valo.core.graphql.$GraphQLFactory$GraphQLInvocation1$Definition.instantiate(Unknown Source)
at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2309)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2279)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2291)
at io.micronaut.context.DefaultBeanContext.createRegistration(DefaultBeanContext.java:3054)
at io.micronaut.context.DefaultBeanContext$5.create(DefaultBeanContext.java:3032)
at io.micronaut.runtime.context.scope.refresh.RefreshScope.lambda$getOrCreate$0(RefreshScope.java:88)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at io.micronaut.runtime.context.scope.refresh.RefreshScope.getOrCreate(RefreshScope.java:87)
at io.micronaut.context.DefaultBeanContext.getOrCreateScopedRegistration(DefaultBeanContext.java:3015)
at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2941)
at io.micronaut.context.DefaultBeanContext.getProxyTargetBean(DefaultBeanContext.java:1489)
at com.devsoap.valo.core.graphql.$GraphQLFactory$GraphQLInvocation1$Definition$Intercepted.interceptedTarget(Unknown Source)
at com.devsoap.valo.core.graphql.$GraphQLFactory$GraphQLInvocation1$Definition$Intercepted.invoke(Unknown Source)
at io.micronaut.configuration.graphql.GraphQLController.executeRequest(GraphQLController.java:200)
at io.micronaut.configuration.graphql.GraphQLController.post(GraphQLController.java:154)
at io.micronaut.configuration.graphql.$GraphQLController$Definition$Exec.dispatch(Unknown Source)
at io.micronaut.context.AbstractExecutableMethodsDefinition$DispatchedExecutableMethod.invokeUnsafe(AbstractExecutableMethodsDefinition.java:447)
at io.micronaut.context.DefaultBeanContext$BeanContextUnsafeExecutionHandle.invokeUnsafe(DefaultBeanContext.java:4214)
at io.micronaut.web.router.AbstractRouteMatch.execute(AbstractRouteMatch.java:263)
at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:74)
at io.micronaut.http.server.RouteExecutor.executeRouteAndConvertBody(RouteExecutor.java:480)
at io.micronaut.http.server.RouteExecutor.callRoute(RouteExecutor.java:468)
at io.micronaut.http.server.RequestLifecycle.lambda$normalFlow$2(RequestLifecycle.java:146)
at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
at io.micronaut.http.server.RequestLifecycle.lambda$normalFlow$4(RequestLifecycle.java:146)
at io.micronaut.http.server.RequestLifecycle.lambda$runWithFilters$14(RequestLifecycle.java:264)
at io.micronaut.http.filter.TerminalFilter.processRequestFilter(TerminalFilter.java:58)
at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:153)
at io.micronaut.http.filter.FilterRunner.lambda$filterRequest0$2(FilterRunner.java:153)
at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
at io.micronaut.http.filter.MethodFilter.processRequestFilter(MethodFilter.java:263)
at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:153)
at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:153)
at io.micronaut.http.filter.FilterRunner.lambda$filterRequest0$2(FilterRunner.java:153)
at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
at io.micronaut.http.filter.MethodFilter.processRequestFilter(MethodFilter.java:263)
at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:153)
at io.micronaut.http.filter.FilterRunner.filterRequest(FilterRunner.java:137)
at io.micronaut.http.filter.FilterRunner.run(FilterRunner.java:132)
at io.micronaut.http.server.RequestLifecycle.runWithFilters(RequestLifecycle.java:281)
at io.micronaut.http.server.RequestLifecycle.normalFlow(RequestLifecycle.java:143)
at io.micronaut.http.server.netty.NettyRequestLifecycle.handleNormal(NettyRequestLifecycle.java:85)
at io.micronaut.http.server.netty.RoutingInBoundHandler.accept(RoutingInBoundHandler.java:220)
at io.micronaut.http.server.netty.websocket.NettyServerWebSocketUpgradeHandler.accept(NettyServerWebSocketUpgradeHandler.java:156)
at io.micronaut.http.server.netty.handler.PipeliningServerHandler$OptimisticBufferingInboundHandler.read(PipeliningServerHandler.java:422)
at io.micronaut.http.server.netty.handler.PipeliningServerHandler.channelRead(PipeliningServerHandler.java:206)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:88)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
at io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalStateException: Recursive update
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1763)
at io.micronaut.runtime.context.scope.refresh.RefreshScope.getOrCreate(RefreshScope.java:87)
at io.micronaut.context.DefaultBeanContext.getOrCreateScopedRegistration(DefaultBeanContext.java:3015)
at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2941)
at io.micronaut.context.DefaultBeanContext.getProxyTargetBean(DefaultBeanContext.java:1489)
at com.devsoap.valo.core.graphql.$GraphQLSchemaBuilder$Definition$Intercepted.interceptedTarget(Unknown Source)
at com.devsoap.valo.core.graphql.$GraphQLSchemaBuilder$Definition$Intercepted.getSchema(Unknown Source)
at com.devsoap.valo.core.graphql.GraphQLFactory.graphQL(GraphQLFactory.java:35)
at com.devsoap.valo.core.graphql.$GraphQLFactory$GraphQL0$Definition.instantiate(Unknown Source)
at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2309)
... 102 common frames omitted
``
Rare occurrence of "Recursive update"
After #5384, it can fail to instantiate beans of RequestScope depending on other RequestScope beans. It does not occur deteministically, but if it occurs, it continues to occur.
I am very sorry not to attach code to reproduce, because this occurred in production and as the product is not open, I cannot share it, and I have little time to write new reproduction code.
Instead, I describe what I investigated. If I get time, I may add reproduction code.
Task List
Steps to Reproduce
@RequestScope
, instantiated by@Factory
@Factory
uses another bean of@RequestScope
to instatiate the bean aboveExpected Behaviour
Instantiation of beans of RequestScope depending on other RequestScope beans succeeds.
Actual Behaviour
BeanInstantiationException with the inner exception 'java.lang.IllegalStateException: Recursive update' is thrown.
Stack trace from the inner exception (some code masked):
What I investigated:
where ConcurrentHashMap is the attribute 'io.micronaut.http.SCOPED_BEANS' of HTTP requests that stores instantiated RequestScope beans.
Environment Information
Example Application