Search code examples
javaspringspring-mvccouchbasespring-cache

How to disable Caching at runtime if Couchbase connection failed?


I have a similar problem as asked here - How to disable Redis Caching at run time if redis connection failed. My application is using @Cacheable at the service layer for most of the database/static resources call.

Cache is backed by Couchbase and whenever application fails to connect Couchbase node application goes down. Which is what we are not expecting, we expect data should be served from the source system whenever connection failed.

We tried implementing CacheErrorHandler but it does not work as expected because we want to execute the actual method which is making a service call and return the response rather than logging the Cache fail, basically bypassing the cache and as soon as the Couchbase node is up or connection established get the data from cache.

Any idea how we can achieve it?


Solution

  • Thanks @Daniel Bickler for the suggestion, below is the implementation I written referring @John Blum answer.

    CouchbaseCustomCacheManager:

    import java.util.Map;
    import org.springframework.cache.Cache;
    import com.couchbase.client.spring.cache.CacheBuilder;
    import com.couchbase.client.spring.cache.CouchbaseCacheManager;
    
    public class CouchbaseCustomCacheManager extends CouchbaseCacheManager {
    
        public CouchbaseCustomCacheManager(
                final Map<String, CacheBuilder> initialCaches) {
            super(initialCaches);
        }
    
        @Override
        public Cache getCache(String name) {
            return new CouchbaseCacheWrapper(super.getCache(name));
        }
    
        protected static class CouchbaseCacheWrapper implements Cache {
    
            private final Cache delegate;
    
            public CouchbaseCacheWrapper(Cache couchbaseCache) {
                this.delegate = couchbaseCache;
            }
    
            @Override
            public String getName() {
                try {
                    return delegate.getName();
                } catch (Exception e) {
                    return null;
                }
            }
    
            @Override
            public Object getNativeCache() {
                try {
                    return delegate.getNativeCache();
                } catch (Exception e) {
                    return null;
                }
            }
    
            @Override
            public ValueWrapper get(Object key) {
                try {
                    return delegate.get(key);
                } catch (Exception e) {
                    return null;
                }
            }
    
            @Override
            public <T> T get(Object key, Class<T> type) {
                try {
                    return delegate.get(key, type);
                } catch (Exception e) {
                    return null;
                }
            }
    
            @Override
            public void put(Object key, Object value) {
                try {
                    delegate.put(key, value);
                } catch (Exception e) {
                    try {
                        handleErrors(e);
                    } catch (Exception e1) {
                    }
                }
            }
    
            @Override
            public ValueWrapper putIfAbsent(Object key, Object value) {
                try {
                    return delegate.putIfAbsent(key, value);
                } catch (Exception e) {
                    return null;
                }
            }
    
            @Override
            public void evict(Object key) {
                try {
                    delegate.evict(key);
                } catch (Exception e) {
                    try {
                        handleErrors(e);
                    } catch (Exception e1) {
                    }
                }
            }
    
            @Override
            public void clear() {
                try {
                    delegate.clear();
                } catch (Exception e) {
                    try {
                        handleErrors(e);
                    } catch (Exception e1) {
                    }
                }
            }
    
            protected <T> T handleErrors(Exception e) throws Exception {
                if (e instanceof Exception) {
                    return null;
                } else {
                    throw e;
                }
            }
        }
    
    }
    

    And used it as:

    @Bean
    public CacheManager cacheManager() {
        final Map<String, CacheBuilder> cache = new HashMap<>();
        for (final String appCache : "127.0.0.1,127.0.0.2,127.0.0.3".split(",")) {
         cache.put(appCache, CacheBuilder.newInstance(CouchbaseCluster.create().openBucket(
                        "default", "")));
        }
        return new CouchbaseCustomCacheManager(cache);
    }