In our application we have number of REST API calls out of which some of them are flagged with @Cacheable to cache the response. Cache is backed by Couchbase and whenever application fails to connect Couchbase node then application goes down.
Which is what we are not expecting, we expect data should be served from the source system whenever connection failed, basically bypassing the cache and as soon as the Couchbase node is up or connection established response should be served from cache.
To achieve the same mechanism, I returned “null” whenever connection fails or there is a Cache miss overriding CouchbaseCacheManager, as follows:
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 CacheManager, as follows:
@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); }
Complete source is available on github.