Disabling Caching at Runtime if Couchbase Connection Failed

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.

Advertisements

Fixing com.couchbase.client.java.error.TranscodingException: Could not encode document with ID cache:EMPLOYEE_:1 – Couchbase

If you are using couchbase spring cache then you might get following exception:


Error occured: Details com.couchbase.client.java.error.TranscodingException: Could not encode document with ID cache:EMPLOYEE_:1

This exception is raised by couchbase client when document is not able to serialized, possible use case is when you are caching the resource which contain nested objects and does not implement Serializable, for example:

Employee.java


public class Employee {

   private int id;
   private String name;
   private Address address;
}

Address.java


 public class Address {
   private String city;
   private String country;
}

If you are facing the same problem as me then you can fix it implementing Serializable in all the classes, as follows:

Employee.java

import java.io.Serializable;

public class Employee implements Serializable {

  private static final long serialVersionUID = 1L;
  private int id;
  private String name;
  private Address address;
}

Address.java

import java.io.Serializable;

public class Address implements Serializable {

  private static final long serialVersionUID = 1L;
  private String city;
  private String country;

}

Fixing com.couchbase.client.java.error.DocumentDoesNotExistException – Couchbase

If you are using couchbase spring cache then you might get following exception:


12:16:58,338 ERROR [com.arpit.common.util.Resource] (http-/127.0.0.1:8080-6) Error occured: Details com.couchbase.client.java.error.DocumentDoesNotExistException

This exception is raised by couchbase client when document does not exist in cache and a replace operation is used, possible use case is when you are caching the resource which return null (which was happening for me), for example:

@Override
@Cacheable(value = "EMPLOYEE_", key = "#id")
public Employee getEmployee(int id) {
	return null;
}

If you are facing the same problem as me then you can fix it using the unless attribute of @Cacheable which is available as of Spring 3.2, as follows:

@Override
@Cacheable(value = "EMPLOYEE_", key = "#id", unless = "#result == null")
public Employee getEmployee(int id) {
	return null;
}