Issue after upgrading from version 2.0.4 to 2.0.5+.
Startup fails with:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'cacheManager': Requested bean is currently in creation: Is there an unresolvable circular reference?
I'm instantiating a HazelcastInstance bean manually and instantiation relies upon a org.springframework.data.jpa.repository.JpaRepository bean. This causes a circular reference on the cacheManager bean during initialisation.
@Bean public FooMapStore fooMapStore(FooRepository fooRepository) {}
@Bean public Config config(FooMapStore fooMapStore) {}
@Bean public HazelcastInstance hazelcastInstance(Config config) { }
I've debugged the dependency graph and it looks like this: entityManagerFactory -> cacheManager -> JCacheCacheConfiguration -> hazelcastPropertiesCustomizer -> hazelcastInstance -> config -> fooMapStore -> fooRepository -> (inner bean)#hash -> entityManagerFactory -> cacheManager
Sample application: https://github.com/JorgenRingen/spring-boot-error-cachemanager-currently-in-creation
Any suggestions for workarounds?
Comment From: snicoll
@JorgenRingen the contract with Spring Boot always has been that the cache infrastructure is available before the JPA entity manager kicks in. The main reasoning behind that is that if you don't do this and you use second level cache, Hibernate will attempt to initialize a cache manager and create a duplicate.
Several things:
- The condition that makes sure this happens was faulty and got fixed in 2.0.5, that's why you get a cycle all of a sudden that should always have been there (#14181)
- The second level cache infrastructure has changed quite a lot in recent Hibernate versions and there is an issue (#14586) to offer out-of-the-box support for this that may potentially relax the current arrangement
I don't see how we could support both use case and having the cache infrastructure initialized by the time hibernate starts (if you're using that) makes sense to me. Can't you defer the registration of the MapStore rather than doing it from the get go?
Comment From: JorgenRingen
Okey, I see we should've avoided the cycle in the first place.
Are there any preferable way of deferring the registration? As a first draft I'm doing a "lazy-lookup" from the ApplicationContext on the first call on FooRepository from FooMapStore, but it feels a bit awkward :-)
public class FooMapStore implements MapStore<Long, Foo> {
@Autowired
private ApplicationContext applicationContext;
private FooRepository fooRepository;
@Override
public void store(Long key, Foo value) {
fooRepository().findAll();
}
...
private FooRepository fooRepository() {
if (this.fooRepository == null) {
this.fooRepository = applicationContext.getBean(FooRepository.class);
}
return fooRepository;
}
Comment From: snicoll
Thanks for the feedback. That's not really what I have in mind. I was more thinking about retrieving the HazelcastInstance later and alter its configuration to register an extra store. Is that a use case that Hazelcast supports?
Comment From: JorgenRingen
I thought about registering the MapStore in a BeanPostProcessor, but according to com.hazelcast.config.Config [Config instances] should not be modified after they are used to create HazelcastInstance.
The creation of the HazelcastInstance bean instantiates it (return Hazelcast.getOrCreateHazelcastInstance(config);) so a BeanPostProcessor is too late. I could also add FooRepository to FooMapStore in a BeanPostProcessor but I think that just adds another layer of confusion compared to looking it up from the ApplicationContext.
Comment From: snicoll
@JorgenRingen that's not what this issue indicates. I've tried and got another Hazelcast problem that I don't really understand:
Caused by: com.hazelcast.nio.serialization.HazelcastSerializationException: There is no suitable serializer for class com.example.cachemanagercurrentlyincreationerror.FooMapStore
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.serializerFor(AbstractSerializationService.java:491) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.writeObject(AbstractSerializationService.java:254) ~[hazelcast-3.9.4.jar:3.9.4]
... 44 common frames omitted
I've submitted a PR to your repro project.
Comment From: JorgenRingen
Interesting. This error only occurs when the mapstore is added in the postconstruct. As far as I understand hazelcast does some kind of broadcasting of the config if the config is updated after initialisation and thats what causing the serialization issue. From stacktrace:
Caused by: com.hazelcast.nio.serialization.HazelcastSerializationException: Failed to serialize 'com.hazelcast.config.MapConfig'
at com.hazelcast.internal.serialization.impl.SerializationUtil.handleSerializeException(SerializationUtil.java:75) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toBytes(AbstractSerializationService.java:161) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toBytes(AbstractSerializationService.java:137) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toData(AbstractSerializationService.java:122) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toData(AbstractSerializationService.java:110) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.dynamicconfig.ClusterWideConfigurationService.cloneConfig(ClusterWideConfigurationService.java:227) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.dynamicconfig.ClusterWideConfigurationService.broadcastConfigAsync(ClusterWideConfigurationService.java:219) ~[hazelcast-3.9.4.jar:3.9.4]
at com.hazelcast.internal.dynamicconfig.ClusterWideConfigurationService.broadcastConfig(ClusterWideConfigurationService.java:199) ~[hazelcast-3.9.4.jar:3.9.4]
Notice "broadcastConfigurationAsync".
I'll try to experiment with the deferred registration in the application where the problem originally occurred a bit later. My example-app is over-simplified with regards to hazelcast in order to show the circular reference problem. Might have to do some additional configuration somewhere in order to do updates of the hazelcast-config after hazelcast initialization.
Comment From: lukass77
please re-check this issue is not a duplicate with same root cause as this one - https://github.com/spring-projects/spring-boot/issues/4960 by try to exclude - HazelcastJpaDependencyAutoConfiguration on your spring boot application + if this indeed satisfied solution - please make sure to document this on spring boot hazelcast integration section
Comment From: snicoll
@lukass77 the issue is open and we'd like to make sure this works without any modification on your end. So, no, it's not a duplicate.
Comment From: nkavian
I have a similar issue. I want to initialize the Hazelcast Config with one setting from my database, which then I'm hit with the cyclical issue.
Comment From: StefanPopa02
Any updates on this? I'm facing a similar issue