My spring boot project uses spring-boot-starter-data-redis
for caching on redis, Oracle database for persistent. It runs perfectly with current version 2.7.6. I've just upgraded to 3.0.5. The source code does change pretty much but this configuration for redis stays the same:
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(redisConnectionFactory)
.transactionAware()
.build();
}
Here is how I use @Cacheable
@Autowired
private ConfRepository confRepository;
@Cacheable(value = "configCache", key = "{#root.methodName, #key}")
@Override
public Optional<Config> getByKey(String key) {
return confRepository.findByKey(key);
}
I deploy my service with 2 instances. The problem is when a key
is put to Cacheable
in instance 1 cannot be deserialized by instance 2, i.e in above method, my first request to instance 1 to getByKey
with the key key1
, my second request to instance 2 also with the key1
should hit the cache (get value from redis and return), but I got the stack trace:
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84)
at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:46)
at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:272)
at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:277)
at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:92)
at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
at org.springframework.cache.transaction.TransactionAwareCacheDecorator.get(TransactionAwareCacheDecorator.java:80)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:571)
at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:536)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:402)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:702)
at vn.com.tcbs.h2h.biz.service.impl.ConfigManagerServiceImpl$$SpringCGLIB$$0.getGroupRole(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:211)
at jdk.proxy2/jdk.proxy2.$Proxy201.getGroupRole(Unknown Source)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:78)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:36)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:82)
... 219 common frames omitted
Caused by: java.lang.IllegalStateException: Could not identify any active SessionFactory having UUID 0105e7c4-c59a-46a6-a86e-23a65dde6ed6
at org.hibernate.proxy.pojo.bytebuddy.SerializableProxy.retrieveMatchingSessionFactory(SerializableProxy.java:127)
at org.hibernate.proxy.pojo.bytebuddy.SerializableProxy.readResolve(SerializableProxy.java:113)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1190)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2266)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1733)
at java.base/java.io.ObjectInputStream$FieldValues.<init>(ObjectInputStream.java:2606)
at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2457)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2257)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1733)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:509)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:467)
at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:71)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:73)
... 221 common frames omitted
Can anybody help me in this situation ?
The issue is that in Hibernate 6, the hibernate object caching is not working. So you need to load the full object if it's FetchType.Lazy
and you need Lazy object properties and unproxy
the object before caching.
To unproxy an object use the Hibernate.unproxy
method.
Updated code:
@Cacheable(value = "configCache", key = "{#root.methodName, #key}")
@Override
public Optional<Config> getByKey(String key) {
var config = confRepository.findByKey(key);
var entity = config.isPresent() ? Hibernate.unproxy(config.get(), Config.class) : null;
return Optional.ofNullable(entity);
}
I had a similar issue after upgrading to Hibernate 6 in Spring MVC. By unproxy
the hibernate object before saving it into the cache solved the issue. Also, with spring security session I need to use record/POJO class for authentication object.
If issue still persist then use record/POJO class to be stored in cache. Because mapping of different entity also adds hibernate classes to serialization.
Hibernate 6: Never store hibernate objects to cache when multiple servers uses same cache.
You can use RedisInsight to browse data stored in redis and inspect to get more details.