Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I configure Spring Cache to ignore cache serialization errors?

I'm using Spring Cache with Redis / JDK Serialization as cache backend. I have a method whose result is cached and the cache is populated with some values. In an update to my app, I'm changing the class of the cached object in a way that breaks Java deserialization (changing the type of a member from a List to a Set). Now any invokation to my cached method fails with org.springframework.data.redis.serializer.SerializationException.

@Cacheable(cacheNames = "myCache", cacheManager = "myCacheManager)
public SomeObject myCachedMethod(String param) {
    return ...;
}

Stack Trace:

Caused by: org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.lang.ClassCastException: cannot assign instance of java.util.ArrayList to field SomeObject.foo of type java.util.Set in instance of SomeObject
    at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:41) ~[spring-data-redis-1.6.4.RELEASE.jar:na]
    at org.springframework.data.redis.cache.RedisCache$CacheValueAccessor.deserializeIfNecessary(RedisCache.java:378) ~[spring-data-redis-1.6.4.RELEASE.jar:na]
    at org.springframework.data.redis.cache.RedisCache.get(RedisCache.java:144) ~[spring-data-redis-1.6.4.RELEASE.jar:na]
    at org.springframework.data.redis.cache.RedisCache.get(RedisCache.java:94) ~[spring-data-redis-1.6.4.RELEASE.jar:na]
    at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:68) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:466) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:432) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:336) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:302) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61) ~[spring-context-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at com.sun.proxy.$Proxy175.myCachedMethod(Unknown Source) ~[na:na]

Is there a way to configure Spring cache to ignore error when deserializing objects and let the method invokation go through?

like image 595
Christophe L Avatar asked Jan 06 '23 11:01

Christophe L


1 Answers

Found a solution after reading through Spring's code.

Cache layer handling happens in an instance of org.springframework.cache.interceptor.CacheErrorHandler. By default, Spring uses org.springframework.cache.interceptor.SimpleCacheErrorHandler which throws back all exceptions. To relax this, one can extend that class and log / ignore cache get exceptions (which causes them to be handled like cache miss):

@Configuration
@EnableCaching
public class CacheConfiguration extends CachingConfigurerSupport {
    @Slf4j
    private static class RelaxedCacheErrorHandler extends SimpleCacheErrorHandler {
        @Override
        public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
            log.error("Error getting from cache.", exception);
        }
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return new RelaxedCacheErrorHandler();
    }

    // More config...
}
like image 165
Christophe L Avatar answered Jan 13 '23 08:01

Christophe L