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
Now we get an issue when multiple threads start calling a @Lazy bean during startup, which then calls its FactoryBean in parallel, and if the factory is not thread safe there are issues.
In the case of #35242 it was actually the DiscoveryClient and InstanceInfoReplicator calling HealthIndicator instances in a background thread, which was in turn calling the feign client.
In our case we had something similar where a bean started a cron job with no delay.
Solution?
In my opinion strict locking should be the enabled by default, and if I understand correctly we would (broadly) arrive at state №1. But for those experiencing deadlocks they can disable strict locking, which was impossible before. From the original issue's description this is quite rare, and I would expect it to definitely be rarer than @Lazy FeignClients and other non thread safe factories:
It's tricky and quite complex. In some situations, listening to the GC notification won't cause any bean creation. It will cause bean creation if you're using Prometheus, have Exemplars enabled, and the lazily created SpanContextSupplier implementation hasn't already been created
What do you think about changing the default to strict? If you want to change it for optimization purposes, maybe we can have a grace period where spring requires thread-safe FactoryBean-s and then use non strict as default?
Here's a "reproduction"
// factory classpublicclassUnsafeFactoryBeanimplementsFactoryBean<UnsafeFactoryBean.UnsafeBean> {
privatefinalPhaserphaser = newPhaser(2);
@OverridepublicUnsafeBeangetObject() {
System.out.println("Arrived");
// when two threads hit this, we proceed. With strict=true this never happens, so we hangphaser.arriveAndAwaitAdvance();
returnnewUnsafeBean();
}
@OverridepublicClass<?> getObjectType() {
returnUnsafeBean.class;
}
publicstaticclassUnsafeBean {
publicvoiddoSomething() {
System.out.println("doSomething");
}
}
}
// caller class@Slf4jpublicclassUnsafeBeanCaller {
privatefinalUnsafeFactoryBean.UnsafeBeanunsafeBean;
privatefinalScheduledExecutorServiceexecutorService = Executors.newSingleThreadScheduledExecutor();
publicUnsafeBeanCaller(UnsafeFactoryBean.UnsafeBeanunsafeBean) {
this.unsafeBean = unsafeBean;
check();
}
publicvoidcheck() {
executorService.scheduleWithFixedDelay(() -> {
log.info("Checking unsafe bean...");
unsafeBean.doSomething();
}, 0, 5000, TimeUnit.MILLISECONDS);
}
}
// configpublicclassMyConfiguration {
@BeanpublicUnsafeFactoryBeanunsafeFactoryBean() {
returnnewUnsafeFactoryBean();
}
@BeanpublicUnsafeBeanCallerlazyUnsafeBean(@LazyUnsafeFactoryBean.UnsafeBeanunsafeBean) {
returnnewUnsafeBeanCaller(unsafeBean);
}
@BeanpublicUnsafeBeanCallerunsafeBean(@LazyUnsafeFactoryBean.UnsafeBeanunsafeBean) {
returnnewUnsafeBeanCaller(unsafeBean);
}
}
Context
It seems like there were a lot of changes to the concurrency of singleton creations, here's my understanding of it:
spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
Lines 119 to 121 in 99a366b
spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
Lines 119 to 122 in 384dc2a
spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
Lines 119 to 133 in fa168ca
Problem
Now we get an issue when multiple threads start calling a
@Lazybean during startup, which then calls its FactoryBean in parallel, and if the factory is not thread safe there are issues.In the case of #35242 it was actually the
DiscoveryClientandInstanceInfoReplicatorcallingHealthIndicatorinstances in a background thread, which was in turn calling the feign client.In our case we had something similar where a bean started a cron job with no delay.
Solution?
In my opinion strict locking should be the enabled by default, and if I understand correctly we would (broadly) arrive at state №1. But for those experiencing deadlocks they can disable strict locking, which was impossible before. From the original issue's description this is quite rare, and I would expect it to definitely be rarer than
@LazyFeignClients and other non thread safe factories:What do you think about changing the default to strict? If you want to change it for optimization purposes, maybe we can have a grace period where spring requires thread-safe
FactoryBean-s and then use non strict as default?Here's a "reproduction"