2

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?

Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108
  • What's wrong with [this](https://stackoverflow.com/a/29353566/1495050) answer on that question? It seems to handle the case you need using the Spring caching framework. – Daniel Bickler May 25 '17 at 15:06
  • @DanielBickler answer you mentioned shows how to handle the cache connection failure, but what I am looking for basically bypassing the cache and execute the method defination whenever connection is not available and as soon as connection established get the data from cache. – Arpit Aggarwal May 25 '17 at 15:28
  • Sorry, I was trying to link to [this](https://stackoverflow.com/a/29353566/1495050) answer. Where you try to access the cache and handle the exception. If it is a connection exception then return null. – Daniel Bickler May 25 '17 at 15:31
  • 1
    Sorry, ignore my last comment. I was looking at it on my phone and it took me to the wrong answer. How would you determine the state of the connection? If you can determine the state of the connection, you could do something similar to the first answer where you wrap part of the cache and emulate a cache miss when there is no connection. – Daniel Bickler May 25 '17 at 15:35
  • @DanielBickler thanks for the suggestion "emulate a cache miss when there is no connection" was the key. – Arpit Aggarwal May 25 '17 at 19:44
  • Possible duplicate of [How to disable Redis Caching at run time if redis connection failed](https://stackoverflow.com/questions/29003786/how-to-disable-redis-caching-at-run-time-if-redis-connection-failed) – Daniel Bickler May 25 '17 at 19:45

1 Answers1

1

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);
}
Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108