package com.netflix.evcache;
import static com.netflix.evcache.util.Sneaky.sneakyThrow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.config.ChainedDynamicProperty;
import com.netflix.config.DynamicBooleanProperty;
import com.netflix.evcache.EVCacheInMemoryCache.DataNotFoundException;
import com.netflix.evcache.EVCacheLatch.Policy;
import com.netflix.evcache.event.EVCacheEvent;
import com.netflix.evcache.event.EVCacheEventListener;
import com.netflix.evcache.metrics.EVCacheMetricsFactory;
import com.netflix.evcache.metrics.Operation;
import com.netflix.evcache.metrics.Stats;
import com.netflix.evcache.operation.EVCacheFuture;
import com.netflix.evcache.operation.EVCacheLatchImpl;
import com.netflix.evcache.operation.EVCacheOperationFuture;
import com.netflix.evcache.pool.EVCacheClient;
import com.netflix.evcache.pool.EVCacheClientPool;
import com.netflix.evcache.pool.EVCacheClientPoolManager;
import com.netflix.evcache.pool.EVCacheClientUtil;
import com.netflix.evcache.util.EVCacheConfig;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.monitor.Counter;
import com.netflix.spectator.api.DistributionSummary;
import net.spy.memcached.CachedData;
import net.spy.memcached.transcoders.Transcoder;
import net.spy.memcached.util.StringUtils;
import rx.Observable;
import rx.Scheduler;
import rx.Single;
/**
* An implementation of a ephemeral volatile cache.
*
* @author smadappa
* @version 2.0
*/
@SuppressWarnings("unchecked")
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings({ "PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS", "WMI_WRONG_MAP_ITERATOR",
"DB_DUPLICATE_BRANCHES", "REC_CATCH_EXCEPTION",
"RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" })
final public class EVCacheImpl implements EVCache {
private static Logger log = LoggerFactory.getLogger(EVCacheImpl.class);
private final String _appName;
private final String _cacheName;
private final String _metricPrefix;
private final String _metricName;
private final Transcoder<?> _transcoder;
private final boolean _zoneFallback;
private final boolean _throwException;
private final int _timeToLive; // defaults to 15 minutes
private final EVCacheClientPool _pool;
private final ChainedDynamicProperty.BooleanProperty _throwExceptionFP, _zoneFallbackFP;
private final DynamicBooleanProperty _bulkZoneFallbackFP;
private final DynamicBooleanProperty _bulkPartialZoneFallbackFP;
private final ChainedDynamicProperty.BooleanProperty _useInMemoryCache;
private final Stats stats;
private EVCacheInMemoryCache<?> cache;
private EVCacheClientUtil clientUtil = null;
private final EVCacheClientPoolManager _poolManager;
private DistributionSummary setTTLSummary, replaceTTLSummary, touchTTLSummary, setDataSizeSummary, replaceDataSizeSummary, appendDataSizeSummary;
private Counter touchCounter;
EVCacheImpl(String appName, String cacheName, int timeToLive, Transcoder<?> transcoder, boolean enableZoneFallback,
boolean throwException,
EVCacheClientPoolManager poolManager) {
this._appName = appName;
this._cacheName = cacheName;
this._timeToLive = timeToLive;
this._transcoder = transcoder;
this._zoneFallback = enableZoneFallback;
this._throwException = throwException;
stats = EVCacheMetricsFactory.getStats(appName, cacheName);
_metricName = (_cacheName == null) ? _appName : _appName + "." + _cacheName;
_metricPrefix = _appName + "-";
this._poolManager = poolManager;
this._pool = poolManager.getEVCacheClientPool(_appName);
final EVCacheConfig config = EVCacheConfig.getInstance();
_throwExceptionFP = config.getChainedBooleanProperty(_metricName + ".throw.exception", _appName + ".throw.exception", Boolean.FALSE);
_zoneFallbackFP = config.getChainedBooleanProperty(_metricName + ".fallback.zone", _appName + ".fallback.zone", Boolean.TRUE);
_bulkZoneFallbackFP = config.getDynamicBooleanProperty(_appName + ".bulk.fallback.zone", true);
_bulkPartialZoneFallbackFP = config.getDynamicBooleanProperty(_appName+ ".bulk.partial.fallback.zone", true);
_useInMemoryCache = config.getChainedBooleanProperty(_appName + ".use.inmemory.cache", "evcache.use.inmemory.cache", Boolean.FALSE);
_pool.pingServers();
}
private String getCanonicalizedKey(String key) {
final String cKey;
if (this._cacheName == null) {
cKey = key;
} else {
cKey = _cacheName + ':' + key;
}
StringUtils.validateKey(cKey, false);
return cKey;
}
private String getKey(String canonicalizedKey) {
if (canonicalizedKey == null) return canonicalizedKey;
if (_cacheName == null) return canonicalizedKey;
final String _cacheNameDelimited = _cacheName + ':';
return canonicalizedKey.replaceFirst(_cacheNameDelimited, "");
}
private boolean hasZoneFallbackForBulk() {
if (!_pool.supportsFallback()) return false;
if (!_bulkZoneFallbackFP.get()) return false;
return _zoneFallback;
}
private boolean hasZoneFallback() {
if (!_pool.supportsFallback()) return false;
if (!_zoneFallbackFP.get().booleanValue()) return false;
return _zoneFallback;
}
private boolean shouldLog() {
return _poolManager.shouldLog(_appName);
}
private boolean doThrowException() {
return (_throwException || _throwExceptionFP.get().booleanValue());
}
private List<EVCacheEventListener> getEVCacheEventListeners() {
return _poolManager.getEVCacheEventListeners();
}
private EVCacheEvent createEVCacheEvent(Collection<EVCacheClient> clients, Collection<String> keys, Call call) {
final List<EVCacheEventListener> evcacheEventListenerList = getEVCacheEventListeners();
if (evcacheEventListenerList == null || evcacheEventListenerList.size() == 0) return null;
final EVCacheEvent event = new EVCacheEvent(call, _appName, _cacheName);
event.setKeys(keys);
event.setClients(clients);
return event;
}
private boolean shouldThrottle(EVCacheEvent event) throws EVCacheException {
for (EVCacheEventListener evcacheEventListener : getEVCacheEventListeners()) {
if (evcacheEventListener.onThrottle(event)) {
return true;
}
}
return false;
}
private void startEvent(EVCacheEvent event) {
final List<EVCacheEventListener> evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onStart(event);
}
}
private void endEvent(EVCacheEvent event) {
final List<EVCacheEventListener> evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onComplete(event);
}
}
private void eventError(EVCacheEvent event, Throwable t) {
final List<EVCacheEventListener> evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onError(event, t);
}
}
private <T> EVCacheInMemoryCache<T> getInMemoryCache(Transcoder<T> tc) {
if (cache == null) cache = _poolManager.createInMemoryCache(_appName, tc, this);
return (EVCacheInMemoryCache<T>) cache;
}
public <T> T get(String key) throws EVCacheException {
return this.get(key, (Transcoder<T>) _transcoder);
}
private void increment(String metric) {
increment(null, null, _metricPrefix + metric);
}
private void increment(String serverGroup, String cachePrefix, String metric) {
EVCacheMetricsFactory.increment(_appName, cachePrefix, serverGroup, _metricPrefix + metric);
}
public <T> T get(String key, Transcoder<T> tc) throws EVCacheException {
if (null == key) throw new IllegalArgumentException("Key cannot be null");
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
T value = null;
try {
value = (T) getInMemoryCache(tc).get(canonicalKey);
} catch (ExecutionException e) {
final boolean throwExc = doThrowException();
if(throwExc) {
if(e.getCause() instanceof DataNotFoundException) {
return null;
}
if(e.getCause() instanceof EVCacheException) {
if (log.isDebugEnabled() && shouldLog()) log.debug("ExecutionException while getting data from InMemory Cache", e);
throw (EVCacheException)e.getCause();
}
throw new EVCacheException("ExecutionException", e);
}
}
if (log.isDebugEnabled() && shouldLog()) log.debug("Value retrieved from inmemory cache for APP " + _appName + ", key : " + canonicalKey + (log.isTraceEnabled() ? "; value : " + value : ""));
if (value != null) return value;
}
return doGet(canonicalKey, tc);
}
<T> T doGet(String canonicalKey , Transcoder<T> tc) throws EVCacheException {
final boolean throwExc = doThrowException();
EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to get the data APP " + _appName);
return null; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Collections.singletonList(canonicalKey), Call.GET);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + canonicalKey);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.GET, stats, Operation.TYPE.MILLI);
try {
final boolean hasZF = hasZoneFallback();
boolean throwEx = hasZF ? false : throwExc;
T data = getData(client, canonicalKey, tc, throwEx, hasZF);
if (data == null && hasZF) {
final List<EVCacheClient> fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
if (fbClients != null && !fbClients.isEmpty()) {
for (int i = 0; i < fbClients.size(); i++) {
final EVCacheClient fbClient = fbClients.get(i);
if(i >= fbClients.size() - 1) throwEx = throwExc;
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + canonicalKey);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
}
data = getData(fbClient, canonicalKey, tc, throwEx, (i < fbClients.size() - 1) ? true : false);
if (log.isDebugEnabled() && shouldLog()) log.debug("Retry for APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + fbClient.getServerGroup());
if (data != null) {
client = fbClient;
break;
}
}
increment(client.getServerGroupName(), _cacheName, "RETRY_" + ((data == null) ? "MISS" : "HIT"));
}
}
if (data != null) {
stats.cacheHit(Call.GET);
if (event != null) event.setAttribute("status", "GHIT");
} else {
stats.cacheMiss(Call.GET);
if (event != null) event.setAttribute("status", "GMISS");
if (log.isInfoEnabled() && shouldLog()) log.info("GET : APP " + _appName + " ; cache miss for key : " + canonicalKey);
}
if (log.isDebugEnabled() && shouldLog()) log.debug("GET : APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + client.getServerGroup());
if (event != null) endEvent(event);
return data;
} catch (net.spy.memcached.internal.CheckedOperationTimeoutException ex) {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("CheckedOperationTimeoutException getting data for APP " + _appName + ", key = "
+ canonicalKey
+ ".\nYou can set the following property to increase the timeout " + _appName
+ ".EVCacheClientPool.readTimeout=<timeout in milli-seconds>", ex);
} catch (Exception ex) {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("Exception getting data for APP " + _appName + ", key = " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("GET : APP " + _appName + ", Took " + op.getDuration() + " milliSec.");
}
}
public <T> Single<T> get(String key, Scheduler scheduler) {
return this.get(key, (Transcoder<T>) _transcoder, scheduler);
}
public <T> Single<T> get(String key, Transcoder<T> tc, Scheduler scheduler) {
if (null == key) return Single.error(new IllegalArgumentException("Key cannot be null"));
final boolean throwExc = doThrowException();
final EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
return Single.error(new EVCacheException("Could not find a client to get the data APP " + _appName));
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Collections.singletonList(key), Call.GET);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
return Single.error(new EVCacheException("Request Throttled for app " + _appName + " & key " + key));
}
} catch(EVCacheException ex) {
throw sneakyThrow(ex);
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.GET, stats, Operation.TYPE.MILLI);
final boolean hasZF = hasZoneFallback();
boolean throwEx = hasZF ? false : throwExc;
return getData(client, canonicalKey, tc, throwEx, hasZF, scheduler).flatMap(data -> {
if (data == null && hasZF) {
final List<EVCacheClient> fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
if (fbClients != null && !fbClients.isEmpty()) {
return Observable.concat(Observable.from(fbClients).map(
fbClient -> getData(fbClients.indexOf(fbClient), fbClients.size(), fbClient, canonicalKey, tc, throwEx, throwExc, false, scheduler) //TODO : for the last one make sure to pass throwExc
.doOnSuccess(fbData -> increment(fbClient.getServerGroupName(), _cacheName, "RETRY_" + ((fbData == null) ? "MISS" : "HIT")))
.toObservable()))
.firstOrDefault(null, fbData -> (fbData != null)).toSingle();
}
}
return Single.just(data);
}).map(data -> {
if (data != null) {
stats.cacheHit(Call.GET);
if (event != null) event.setAttribute("status", "GHIT");
} else {
stats.cacheMiss(Call.GET);
if (event != null) event.setAttribute("status", "GMISS");
if (log.isInfoEnabled() && shouldLog())
log.info("GET : APP " + _appName + " ; cache miss for key : " + canonicalKey);
}
if (log.isDebugEnabled() && shouldLog()) log.debug("GET : APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + client .getServerGroup());
if (event != null) endEvent(event);
return data;
}).onErrorReturn(ex -> {
if (ex instanceof net.spy.memcached.internal.CheckedOperationTimeoutException) {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("CheckedOperationTimeoutException getting data for APP " + _appName + ", key = "
+ canonicalKey
+ ".\nYou can set the following property to increase the timeout " + _appName
+ ".EVCacheClientPool.readTimeout=<timeout in milli-seconds>", ex));
} else {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("Exception getting data for APP " + _appName + ", key = " + canonicalKey, ex));
}
}).doAfterTerminate(() -> {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("GET : APP " + _appName + ", Took " + op.getDuration() + " milliSec.");
});
}
private <T> T getData(EVCacheClient client, String canonicalKey, Transcoder<T> tc, boolean throwException, boolean hasZF) throws Exception {
if (client == null) return null;
try {
if(tc == null && _transcoder != null) tc = (Transcoder<T>)_transcoder;
return client.get(canonicalKey, tc, throwException, hasZF);
} catch (EVCacheReadQueueException ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("EVCacheReadQueueException while getting data for APP " + _appName + ", key : " + canonicalKey + "; hasZF : " + hasZF, ex);
if (!throwException || hasZF) return null;
throw ex;
} catch (EVCacheException ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("EVCacheException while getting data for APP " + _appName + ", key : " + canonicalKey + "; hasZF : " + hasZF, ex);
if (!throwException || hasZF) return null;
throw ex;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception while getting data for APP " + _appName + ", key : " + canonicalKey, ex);
if (!throwException || hasZF) return null;
throw ex;
}
}
private <T> Single<T> getData(int index, int size, EVCacheClient client, String canonicalKey, Transcoder<T> tc, boolean throwEx, boolean throwExc, boolean hasZF, Scheduler scheduler) {
if(index >= size -1) throwEx = throwExc;
return getData(client, canonicalKey, tc, throwEx, hasZF, scheduler);
}
private <T> Single<T> getData(EVCacheClient client, String canonicalKey, Transcoder<T> tc, boolean throwException, boolean hasZF, Scheduler scheduler) {
if (client == null) return Single.error(new IllegalArgumentException("Client cannot be null"));
if(tc == null && _transcoder != null) tc = (Transcoder<T>)_transcoder;
return client.get(canonicalKey, tc, throwException, hasZF, scheduler).onErrorReturn(ex -> {
if (ex instanceof EVCacheReadQueueException) {
if (log.isDebugEnabled() && shouldLog()) log.debug("EVCacheReadQueueException while getting data for APP " + _appName + ", key : " + canonicalKey + "; hasZF : " + hasZF, ex);
if (!throwException || hasZF) return null;
throw sneakyThrow(ex);
} else if (ex instanceof EVCacheException) {
if (log.isDebugEnabled() && shouldLog()) log.debug("EVCacheException while getting data for APP " + _appName + ", key : " + canonicalKey + "; hasZF : " + hasZF, ex);
if (!throwException || hasZF) return null;
throw sneakyThrow(ex);
} else {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception while getting data for APP " + _appName + ", key : " + canonicalKey, ex);
if (!throwException || hasZF) return null;
throw sneakyThrow(ex);
}
});
}
public <T> T getAndTouch(String key, int timeToLive) throws EVCacheException {
return this.getAndTouch(key, timeToLive, (Transcoder<T>) _transcoder);
}
public <T> Single<T> getAndTouch(String key, int timeToLive, Scheduler scheduler) {
return this.getAndTouch(key, timeToLive, (Transcoder<T>) _transcoder, scheduler);
}
public <T> Single<T> getAndTouch(String key, int timeToLive, Transcoder<T> tc, Scheduler scheduler) {
if (null == key) return Single.error(new IllegalArgumentException("Key cannot be null"));
final boolean throwExc = doThrowException();
final EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
return Single.error(new EVCacheException("Could not find a client to get and touch the data for APP " + _appName));
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Collections.singletonList(key), Call.GET_AND_TOUCH);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
return Single.error(new EVCacheException("Request Throttled for app " + _appName + " & key " + key));
}
} catch(EVCacheException ex) {
throw sneakyThrow(ex);
}
event.setTTL(timeToLive);
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.GET_AND_TOUCH, stats, Operation.TYPE.MILLI);
final boolean hasZF = hasZoneFallback();
boolean throwEx = hasZF ? false : throwExc;
//anyway we have to touch all copies so let's just reuse getData instead of getAndTouch
return getData(client, canonicalKey, tc, throwEx, hasZF, scheduler).flatMap(data -> {
if (data == null && hasZF) {
final List<EVCacheClient> fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
if (fbClients != null && !fbClients.isEmpty()) {
return Observable.concat(Observable.from(fbClients).map(
fbClient -> getData(fbClients.indexOf(fbClient), fbClients.size(), fbClient, canonicalKey, tc, throwEx, throwExc, false, scheduler) //TODO : for the last one make sure to pass throwExc
.doOnSuccess(fbData -> increment(fbClient.getServerGroupName(), _cacheName, "RETRY_" + ((fbData == null) ? "MISS" : "HIT")))
.toObservable()))
.firstOrDefault(null, fbData -> (fbData != null)).toSingle();
}
}
return Single.just(data);
}).map(data -> {
if (data != null) {
stats.cacheHit(Call.GET_AND_TOUCH);
if (event != null) event.setAttribute("status", "THIT");
// touch all copies
try {
touchData(canonicalKey, key, timeToLive);
} catch (Exception e) {
throw sneakyThrow(new EVCacheException("Exception performing touch for APP " + _appName + ", key = " + canonicalKey, e));
}
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + client .getServerGroup());
} else {
stats.cacheMiss(Call.GET_AND_TOUCH);
if (event != null) event.setAttribute("status", "TMISS");
if (log.isInfoEnabled() && shouldLog()) log.info("GET_AND_TOUCH : APP " + _appName + " ; cache miss for key : " + canonicalKey);
}
if (event != null) endEvent(event);
return data;
}).onErrorReturn(ex -> {
if (ex instanceof net.spy.memcached.internal.CheckedOperationTimeoutException) {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("CheckedOperationTimeoutException executing getAndTouch APP " + _appName + ", key = " + canonicalKey
+ ".\nYou can set the following property to increase the timeout " + _appName + ".EVCacheClientPool.readTimeout=<timeout in milli-seconds>", ex));
} else {
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("Exception executing getAndTouch APP " + _appName + ", key = " + canonicalKey, ex));
}
}).doAfterTerminate(() -> {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", Took " + op.getDuration() + " milliSec.");
});
}
@Override
public <T> T getAndTouch(String key, int timeToLive, Transcoder<T> tc) throws EVCacheException {
if (null == key) throw new IllegalArgumentException("Key cannot be null");
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
final boolean throwExc = doThrowException();
T value = null;
try {
value = (T) getInMemoryCache(tc).get(canonicalKey);
} catch (ExecutionException e) {
if(throwExc) {
if(e.getCause() instanceof DataNotFoundException) {
return null;
}
if(e.getCause() instanceof EVCacheException) {
if (log.isDebugEnabled() && shouldLog()) log.debug("ExecutionException while getting data from InMemory Cache", e);
throw (EVCacheException)e.getCause();
}
throw new EVCacheException("ExecutionException", e);
}
}
if (value != null) {
try {
touchData(canonicalKey, key, timeToLive);
} catch (Exception e) {
if (throwExc) throw new EVCacheException("Exception executing getAndTouch APP " + _appName + ", key = " + canonicalKey, e);
}
return value;
}
}
return doGetAndTouch(canonicalKey, key, timeToLive, tc);
}
<T> T doGetAndTouch(String canonicalKey, String key, int timeToLive, Transcoder<T> tc) throws EVCacheException {
final boolean throwExc = doThrowException();
EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to get and touch the data for App " + _appName);
return null; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Collections.singletonList(canonicalKey), Call.GET_AND_TOUCH);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + canonicalKey);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
event.setTTL(timeToLive);
startEvent(event);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.GET_AND_TOUCH, stats, Operation.TYPE.MILLI);
try {
final boolean hasZF = hasZoneFallback();
boolean throwEx = hasZF ? false : throwExc;
//T data = getAndTouchData(client, canonicalKey, tc, throwEx, hasZF, timeToLive);
T data = getData(client, canonicalKey, tc, throwEx, hasZF);
if (data == null && hasZF) {
final List<EVCacheClient> fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
for (int i = 0; i < fbClients.size(); i++) {
final EVCacheClient fbClient = fbClients.get(i);
if(i >= fbClients.size() - 1) throwEx = throwExc;
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + canonicalKey);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
}
data = getData(fbClient, canonicalKey, tc, throwEx, (i < fbClients.size() - 1) ? true : false);
if (log.isDebugEnabled() && shouldLog()) log.debug("GetAndTouch Retry for APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + fbClient.getServerGroup());
if (data != null) {
client = fbClient;
break;
}
}
increment(client.getServerGroupName(), _cacheName, "RETRY_" + ((data == null) ? "MISS" : "HIT"));
}
if (data != null) {
stats.cacheHit(Call.GET_AND_TOUCH);
if (event != null) event.setAttribute("status", "THIT");
// touch all copies
touchData(canonicalKey, key, timeToLive);
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", key [" + canonicalKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + client.getServerGroup());
} else {
stats.cacheMiss(Call.GET_AND_TOUCH);
if (log.isInfoEnabled() && shouldLog()) log.info("GET_AND_TOUCH : APP " + _appName + " ; cache miss for key : " + canonicalKey);
if (event != null) event.setAttribute("status", "TMISS");
}
if (event != null) endEvent(event);
return data;
} catch (net.spy.memcached.internal.CheckedOperationTimeoutException ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("CheckedOperationTimeoutException executing getAndTouch APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("CheckedOperationTimeoutException executing getAndTouch APP " + _appName + ", key = " + canonicalKey
+ ".\nYou can set the following property to increase the timeout " + _appName+ ".EVCacheClientPool.readTimeout=<timeout in milli-seconds>", ex);
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception executing getAndTouch APP " + _appName + ", key = " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("Exception executing getAndTouch APP " + _appName + ", key = " + canonicalKey,
ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("Took " + op.getDuration() + " milliSec to get&Touch the value for APP " + _appName + ", key " + canonicalKey);
}
}
@Override
public Future<Boolean>[] touch(String key, int timeToLive) throws EVCacheException {
final EVCacheLatch latch = this.touch(key, timeToLive, null);
if (latch == null) return new EVCacheFuture[0];
final List<Future<Boolean>> futures = latch.getAllFutures();
if (futures == null || futures.isEmpty()) return new EVCacheFuture[0];
final EVCacheFuture[] eFutures = new EVCacheFuture[futures.size()];
for (int i = 0; i < futures.size(); i++) {
final Future<Boolean> future = futures.get(i);
if (future instanceof EVCacheFuture) {
eFutures[i] = (EVCacheFuture) future;
} else if (future instanceof EVCacheOperationFuture) {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, ((EVCacheOperationFuture<Boolean>) futures.get(i)).getServerGroup());
} else {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, null);
}
}
return eFutures;
}
public <T> EVCacheLatch touch(String key, int timeToLive, Policy policy) throws EVCacheException {
if (null == key) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to set the data");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.TOUCH);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
try {
final EVCacheLatchImpl latch = new EVCacheLatchImpl(policy == null ? Policy.ALL_MINUS_1 : policy, clients.length - _pool.getWriteOnlyEVCacheClients().length, _appName);
touchData(canonicalKey, key, timeToLive, clients, latch);
if (touchTTLSummary == null) this.touchTTLSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-TouchData-TTL", _appName, null);
if (touchTTLSummary != null) touchTTLSummary.record(timeToLive);
if (touchCounter == null) this.touchCounter = EVCacheMetricsFactory.getCounter(_appName, _cacheName, _metricPrefix + "TouchCall", DataSourceType.COUNTER);
if (touchCounter != null) touchCounter.increment();
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setTTL(timeToLive);
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception touching the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception setting data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
if (log.isDebugEnabled() && shouldLog()) log.debug("TOUCH : APP " + _appName + " for key : " + canonicalKey + " with ttl : " + timeToLive);
}
}
private EVCacheFuture[] touchData(String canonicalKey, String key, int timeToLive) throws Exception {
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
return touchData(canonicalKey, key, timeToLive, clients);
}
private EVCacheFuture[] touchData(String canonicalKey, String key, int timeToLive, EVCacheClient[] clients) throws Exception {
return touchData(canonicalKey, key, timeToLive, clients, null);
}
private EVCacheFuture[] touchData(String canonicalKey, String key, int timeToLive, EVCacheClient[] clients, EVCacheLatch latch ) throws Exception {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
int index = 0;
for (EVCacheClient client : clients) {
final Future<Boolean> future = client.touch(canonicalKey, timeToLive, latch);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
}
return futures;
}
public <T> Future<T> getAsynchronous(String key) throws EVCacheException {
return this.getAsynchronous(key, (Transcoder<T>) _transcoder);
};
@Override
public <T> Future<T> getAsynchronous(String key, Transcoder<T> tc) throws EVCacheException {
if (null == key) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to asynchronously get the data");
return null; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Collections.singletonList(key),
Call.ASYNC_GET);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final Future<T> r;
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.ASYNC_GET, stats,
Operation.TYPE.MILLI);
try {
final String canonicalKey = getCanonicalizedKey(key);
if(tc == null && _transcoder != null) tc = (Transcoder<T>)_transcoder;
r = client.asyncGet(canonicalKey, tc, throwExc, false);
if (event != null) endEvent(event);
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug( "Exception while getting data for keys Asynchronously APP " + _appName + ", key : " + key, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("Exception getting data for APP " + _appName + ", key : " + key, ex);
} finally {
op.stop();
}
return r;
}
private <T> Map<String, T> getBulkData(EVCacheClient client, Collection<String> canonicalKeys, Transcoder<T> tc,
boolean throwException, boolean hasZF) throws Exception {
try {
if(tc == null && _transcoder != null) tc = (Transcoder<T>)_transcoder;
return client.getBulk(canonicalKeys, tc, throwException, hasZF);
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception while getBulk data for APP " + _appName + ", key : " + canonicalKeys, ex);
if (!throwException || hasZF) return null;
throw ex;
}
}
public <T> Map<String, T> getBulk(Collection<String> keys, Transcoder<T> tc) throws EVCacheException {
return getBulk(keys, tc, false, 0);
}
public <T> Map<String, T> getBulkAndTouch(Collection<String> keys, Transcoder<T> tc, int timeToLive)
throws EVCacheException {
return getBulk(keys, tc, true, timeToLive);
}
private <T> Map<String, T> getBulk(Collection<String> keys, Transcoder<T> tc, boolean touch, int ttl)
throws EVCacheException {
if (null == keys) throw new IllegalArgumentException();
if (keys.isEmpty()) return Collections.<String, T> emptyMap();
final boolean throwExc = doThrowException();
EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to get the data in bulk");
return Collections.<String, T> emptyMap();// Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), keys, Call.BULK);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & keys " + keys);
return Collections.<String, T> emptyMap();
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
event.setTTL(ttl);
startEvent(event);
}
final Collection<String> canonicalKeys = new ArrayList<String>();
/* Canonicalize keys and perform fast failure checking */
for (String k : keys) {
final String canonicalK = getCanonicalizedKey(k);
canonicalKeys.add(canonicalK);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.BULK, stats, Operation.TYPE.MILLI);
try {
final boolean hasZF = hasZoneFallbackForBulk();
boolean throwEx = hasZF ? false : throwExc;
increment(client.getServerGroupName(), _cacheName, "BULK_GET");
Map<String, T> retMap = getBulkData(client, canonicalKeys, tc, throwEx, hasZF);
List<EVCacheClient> fbClients = null;
if (hasZF) {
if (retMap == null || retMap.isEmpty()) {
fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
if (fbClients != null && !fbClients.isEmpty()) {
for (int i = 0; i < fbClients.size(); i++) {
final EVCacheClient fbClient = fbClients.get(i);
if(i >= fbClients.size() - 1) throwEx = throwExc;
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + canonicalKeys);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
}
retMap = getBulkData(fbClient, canonicalKeys, tc, throwEx, (i < fbClients.size() - 1) ? true : false);
if (log.isDebugEnabled() && shouldLog()) log.debug("Fallback for APP " + _appName + ", key [" + canonicalKeys + (log.isTraceEnabled() ? "], Value [" + retMap : "") + "], zone : " + fbClient.getZone());
if (retMap != null && !retMap.isEmpty()) break;
}
increment(client.getServerGroupName(), _cacheName, "BULK_GET-FULL_RETRY-" + ((retMap == null || retMap.isEmpty()) ? "MISS" : "HIT"));
}
}
if (retMap != null && keys.size() > retMap.size() && _bulkPartialZoneFallbackFP.get()) {
final int initRetMapSize = retMap.size();
final int initRetrySize = keys.size() - retMap.size();
List<String> retryKeys = new ArrayList<String>(initRetrySize);
for (Iterator<String> keysItr = canonicalKeys.iterator(); keysItr.hasNext();) {
final String key = keysItr.next();
if (!retMap.containsKey(key)) {
retryKeys.add(key);
}
}
fbClients = _pool.getEVCacheClientsForReadExcluding(client.getServerGroup());
if (fbClients != null && !fbClients.isEmpty()) {
for (int ind = 0; ind < fbClients.size(); ind++) {
final EVCacheClient fbClient = fbClients.get(ind);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & keys " + retryKeys);
return null;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
}
final Map<String, T> fbRetMap = getBulkData(fbClient, retryKeys, tc, false, hasZF);
if (log.isDebugEnabled() && shouldLog()) log.debug("Fallback for APP " + _appName + ", key [" + retryKeys + "], Fallback Server Group : " + fbClient .getServerGroup().getName());
for (Map.Entry<String, T> i : fbRetMap.entrySet()) {
retMap.put(i.getKey(), i.getValue());
if (log.isDebugEnabled() && shouldLog()) log.debug("Fallback for APP " + _appName + ", key [" + i.getKey() + (log.isTraceEnabled() ? "], Value [" + i.getValue(): "]"));
}
if (retryKeys.size() == fbRetMap.size()) break;
if (ind < fbClients.size()) {
retryKeys = new ArrayList<String>(keys.size() - retMap.size());
for (Iterator<String> keysItr = canonicalKeys.iterator(); keysItr.hasNext();) {
final String key = keysItr.next();
if (!retMap.containsKey(key)) {
retryKeys.add(key);
}
}
}
}
if (retMap.size() > initRetMapSize) increment(client.getServerGroupName(), _cacheName, "BULK_GET-PARTIAL_RETRY-" + (retMap.isEmpty() ? "MISS" : "HIT"));
}
if (log.isDebugEnabled() && shouldLog() && retMap.size() == keys.size()) log.debug("Fallback SUCCESS for APP " + _appName + ", retMap [" + retMap + "]");
}
}
if (retMap == null || retMap.isEmpty()) {
if (log.isInfoEnabled() && shouldLog()) log.info("BULK : APP " + _appName + " ; Full cache miss for keys : " + keys);
if (event != null) event.setAttribute("status", "BMISS_ALL");
if (retMap != null && retMap.isEmpty()) {
retMap = new HashMap<String, T>();
for (String k : keys) {
retMap.put(k, null);
}
}
stats.cacheMiss(Call.BULK);
/* If both Retry and first request fail Exit Immediately. */
increment(client.getServerGroupName(), _cacheName, "BULK_MISS");
if (event != null) endEvent(event);
return retMap;
}
/* Decanonicalize the keys */
final Map<String, T> decanonicalR = new HashMap<String, T>((canonicalKeys.size() * 4) / 3 + 1);
for (Iterator<String> itr = canonicalKeys.iterator(); itr.hasNext();) {
final String key = itr.next();
final String deCanKey = getKey(key);
final T value = retMap.get(key);
if (value != null) {
decanonicalR.put(deCanKey, value);
if (touch) touchData(key, deCanKey, ttl);
} else if (fbClients != null && fbClients.size() > 0) {
// this ensures the fallback was tried
decanonicalR.put(deCanKey, null);
}
}
if (!decanonicalR.isEmpty()) {
if (decanonicalR.size() == keys.size()) {
stats.cacheHit(Call.BULK);
increment(client.getServerGroupName(), _cacheName, "BULK_HIT");
if (event != null) event.setAttribute("status", "BHIT");
} else {
if (event != null) {
event.setAttribute("status", "BHIT_PARTIAL");
event.setAttribute("BHIT_PARTIAL_KEYS", decanonicalR);
}
increment(client.getServerGroupName(), _cacheName, "BULK_HIT_PARTIAL");
if (log.isInfoEnabled() && shouldLog()) log.info("BULK_HIT_PARTIAL for APP " + _appName + ", keys in cache [" + decanonicalR + "], all keys [" + keys + "]");
}
}
if (log.isDebugEnabled() && shouldLog()) log.debug("APP " + _appName + ", BULK : Data [" + decanonicalR + "]");
if (event != null) endEvent(event);
return decanonicalR;
} catch (net.spy.memcached.internal.CheckedOperationTimeoutException ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("CheckedOperationTimeoutException getting bulk data for APP " + _appName + ", keys : " + canonicalKeys, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("CheckedOperationTimeoutException getting bulk data for APP " + _appName + ", keys = " + canonicalKeys
+ ".\nYou can set the following property to increase the timeout " + _appName + ".EVCacheClientPool.bulkReadTimeout=<timeout in milli-seconds>", ex);
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception getting bulk data for APP " + _appName + ", keys = " + canonicalKeys, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw new EVCacheException("Exception getting bulk data for APP " + _appName + ", keys = " + canonicalKeys, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("BULK : APP " + _appName + " Took " + op.getDuration() + " milliSec to get the value for key " + canonicalKeys);
}
}
public <T> Map<String, T> getBulk(Collection<String> keys) throws EVCacheException {
return (this.getBulk(keys, (Transcoder<T>) _transcoder));
}
public <T> Map<String, T> getBulk(String... keys) throws EVCacheException {
return (this.getBulk(Arrays.asList(keys), (Transcoder<T>) _transcoder));
}
public <T> Map<String, T> getBulk(Transcoder<T> tc, String... keys) throws EVCacheException {
return (this.getBulk(Arrays.asList(keys), tc));
}
@Override
public <T> EVCacheFuture[] set(String key, T value, Transcoder<T> tc, int timeToLive) throws EVCacheException {
final EVCacheLatch latch = this.set(key, value, tc, timeToLive, null);
if (latch == null) return new EVCacheFuture[0];
final List<Future<Boolean>> futures = latch.getAllFutures();
if (futures == null || futures.isEmpty()) return new EVCacheFuture[0];
final EVCacheFuture[] eFutures = new EVCacheFuture[futures.size()];
for (int i = 0; i < futures.size(); i++) {
final Future<Boolean> future = futures.get(i);
if (future instanceof EVCacheFuture) {
eFutures[i] = (EVCacheFuture) future;
} else if (future instanceof EVCacheOperationFuture) {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, ((EVCacheOperationFuture<T>) futures.get(
i)).getServerGroup());
} else {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, null);
}
}
return eFutures;
}
public <T> EVCacheLatch set(String key, T value, Policy policy) throws EVCacheException {
return set(key, value, (Transcoder<T>)_transcoder, _timeToLive, policy);
}
public <T> EVCacheLatch set(String key, T value, int timeToLive, Policy policy) throws EVCacheException {
return set(key, value, (Transcoder<T>)_transcoder, timeToLive, policy);
}
public <T> EVCacheLatch set(String key, T value, Transcoder<T> tc, EVCacheLatch.Policy policy) throws EVCacheException {
return set(key, value, tc, _timeToLive, policy);
}
public <T> EVCacheLatch set(String key, T value, Transcoder<T> tc, int timeToLive, Policy policy) throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to set the data");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.SET);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName);
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.SET, stats, Operation.TYPE.MILLI);
final EVCacheLatchImpl latch = new EVCacheLatchImpl(policy == null ? Policy.ALL_MINUS_1 : policy, clients.length - _pool.getWriteOnlyEVCacheClients().length, _appName);
try {
CachedData cd = null;
for (EVCacheClient client : clients) {
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (setTTLSummary == null) this.setTTLSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-SetData-TTL", _appName, null);
if (setTTLSummary != null) setTTLSummary.record(timeToLive);
if (cd != null) {
if (setDataSizeSummary == null) this.setDataSizeSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-SetData-Size", _appName, null);
if (setDataSizeSummary != null) this.setDataSizeSummary.record(cd.getData().length);
}
}
final Future<Boolean> future = client.set(canonicalKey, cd, timeToLive, latch);
if (log.isDebugEnabled() && shouldLog()) log.debug("SET : APP " + _appName + ", Future " + future + " for key : " + canonicalKey);
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setTTL(timeToLive);
event.setCachedData(cd);
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception setting the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) endEvent(event);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception setting data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("SET : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + canonicalKey);
}
}
public <T> EVCacheFuture[] append(String key, T value, int timeToLive) throws EVCacheException {
return this.append(key, value, null, timeToLive);
}
public <T> EVCacheFuture[] append(String key, T value, Transcoder<T> tc, int timeToLive) throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to set the data");
return new EVCacheFuture[0]; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.APPEND);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheFuture[0];
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.APPEND, stats, Operation.TYPE.MILLI);
try {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
CachedData cd = null;
int index = 0;
for (EVCacheClient client : clients) {
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (cd != null) {
if (appendDataSizeSummary == null) this.appendDataSizeSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-AppendData-Size", _appName, null);
if (appendDataSizeSummary != null) this.appendDataSizeSummary.record(cd.getData().length);
}
}
final Future<Boolean> future = client.append(canonicalKey, cd);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setCachedData(cd);
event.setTTL(timeToLive);
endEvent(event);
}
touchData(canonicalKey, key, timeToLive, clients);
return futures;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception setting the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheFuture[0];
throw new EVCacheException("Exception setting data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("APPEND : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + canonicalKey);
}
}
public <T> EVCacheFuture[] set(String key, T value, Transcoder<T> tc) throws EVCacheException {
return this.set(key, value, tc, _timeToLive);
}
public <T> EVCacheFuture[] set(String key, T value, int timeToLive) throws EVCacheException {
return this.set(key, value, (Transcoder<T>) _transcoder, timeToLive);
}
public <T> EVCacheFuture[] set(String key, T value) throws EVCacheException {
return this.set(key, value, (Transcoder<T>) _transcoder, _timeToLive);
}
public EVCacheFuture[] delete(String key) throws EVCacheException {
final EVCacheLatch latch = this.delete(key, null);
if (latch == null) return new EVCacheFuture[0];
final List<Future<Boolean>> futures = latch.getAllFutures();
if (futures == null || futures.isEmpty()) return new EVCacheFuture[0];
final EVCacheFuture[] eFutures = new EVCacheFuture[futures.size()];
for (int i = 0; i < futures.size(); i++) {
final Future<Boolean> future = futures.get(i);
if (future instanceof EVCacheFuture) {
eFutures[i] = (EVCacheFuture) future;
} else if (future instanceof EVCacheOperationFuture) {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, ((EVCacheOperationFuture<Boolean>) futures.get(i)).getServerGroup());
} else {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, null);
}
}
return eFutures;
}
@Override
public <T> EVCacheLatch delete(String key, Policy policy) throws EVCacheException {
if (key == null) throw new IllegalArgumentException("Key cannot be null");
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to delete the keyAPP " + _appName
+ ", Key " + key);
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.DELETE);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.DELETE, stats);
final EVCacheLatchImpl latch = new EVCacheLatchImpl(policy == null ? Policy.ALL_MINUS_1 : policy, clients.length - _pool.getWriteOnlyEVCacheClients().length, _appName);
try {
for (int i = 0; i < clients.length; i++) {
Future<Boolean> future = clients[i].delete(canonicalKey, latch);
if (log.isDebugEnabled() && shouldLog()) log.debug("DELETE : APP " + _appName + ", Future " + future + " for key : " + canonicalKey);
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception while deleting the data for APP " + _appName + ", key : " + key, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception while deleting the data for APP " + _appName + ", key : " + key, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("DELETE : APP " + _appName + " Took " + op.getDuration() + " milliSec for key : " + key);
}
}
public int getDefaultTTL() {
return _timeToLive;
}
public long incr(String key, long by, long defaultVal, int timeToLive) throws EVCacheException {
if ((null == key) || by < 0 || defaultVal < 0 || timeToLive < 0) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (log.isDebugEnabled() && shouldLog()) log.debug("INCR : " + _metricName + ":NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to incr the data");
return -1;
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key),
Call.INCR);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return -1;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return -1;
}
startEvent(event);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.INCR, stats, Operation.TYPE.MILLI);
try {
final long[] vals = new long[clients.length];
final String canonicalKey = getCanonicalizedKey(key);
int index = 0;
long currentValue = -1;
for (EVCacheClient client : clients) {
vals[index] = client.incr(canonicalKey, by, defaultVal, timeToLive);
if (vals[index] != -1 && currentValue < vals[index]) currentValue = vals[index];
index++;
}
if (currentValue != -1) {
if (log.isDebugEnabled()) log.debug("INCR : APP " + _appName + " current value = " + currentValue + " for key : " + key);
for (int i = 0; i < vals.length; i++) {
if (vals[i] == -1 && currentValue > -1) {
if (log.isDebugEnabled()) log.debug("INCR : APP " + _appName + "; Zone " + clients[i].getZone()
+ " had a value = -1 so setting it to current value = " + currentValue + " for key : " + key);
clients[i].incr(canonicalKey, 0, currentValue, timeToLive);
} else if (vals[i] != currentValue) {
if (log.isDebugEnabled()) log.debug("INCR : APP " + _appName + "; Zone " + clients[i].getZone()
+ " had a value of " + vals[i] + " so setting it to current value = " + currentValue + " for key : " + key);
clients[i].set(canonicalKey, String.valueOf(currentValue), timeToLive);
}
}
}
if (event != null) endEvent(event);
return currentValue;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception incrementing the value for APP " + _appName + ", key : " + key, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return -1;
throw new EVCacheException("Exception incrementing value for APP " + _appName + ", key : " + key, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("INCR : APP " + _appName + ", Took " + op.getDuration()
+ " milliSec for key : " + key);
}
}
public long decr(String key, long by, long defaultVal, int timeToLive) throws EVCacheException {
if ((null == key) || by < 0 || defaultVal < 0 || timeToLive < 0) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (log.isDebugEnabled() && shouldLog()) log.debug("DECR : " + _metricName + ":NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to decr the data");
return -1;
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key),
Call.DECR);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return -1;
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return -1;
}
startEvent(event);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.DECR, stats, Operation.TYPE.MILLI);
try {
final long[] vals = new long[clients.length];
final String canonicalKey = getCanonicalizedKey(key);
int index = 0;
long currentValue = -1;
for (EVCacheClient client : clients) {
vals[index] = client.decr(canonicalKey, by, defaultVal, timeToLive);
if (vals[index] != -1 && currentValue < vals[index]) currentValue = vals[index];
index++;
}
if (currentValue != -1) {
if (log.isDebugEnabled()) log.debug("DECR : APP " + _appName + " current value = " + currentValue
+ " for key : " + key);
for (int i = 0; i < vals.length; i++) {
if (vals[i] == -1 && currentValue > -1) {
if (log.isDebugEnabled()) log.debug("DECR : APP " + _appName + "; Zone " + clients[i].getZone()
+ " had a value = -1 so setting it to current value = "
+ currentValue + " for key : " + key);
clients[i].decr(canonicalKey, 0, currentValue, timeToLive);
} else if (vals[i] != currentValue) {
if (log.isDebugEnabled()) log.debug("DECR : APP " + _appName + "; Zone " + clients[i].getZone()
+ " had a value of " + vals[i]
+ " so setting it to current value = " + currentValue + " for key : " + key);
clients[i].set(canonicalKey, String.valueOf(currentValue), timeToLive);
}
}
}
if (event != null) endEvent(event);
return currentValue;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception decrementing the value for APP " + _appName + ", key : " + key, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return -1;
throw new EVCacheException("Exception decrementing value for APP " + _appName + ", key : " + key, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("DECR : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + key);
}
}
@Override
public <T> EVCacheLatch replace(String key, T value, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder<T>) _transcoder, policy);
}
@Override
public <T> EVCacheLatch replace(String key, T value, Transcoder<T> tc, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder<T>) _transcoder, _timeToLive, policy);
}
public <T> EVCacheLatch replace(String key, T value, int timeToLive, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder<T>)_transcoder, timeToLive, policy);
}
@Override
public <T> EVCacheLatch replace(String key, T value, Transcoder<T> tc, int timeToLive, Policy policy)
throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to set the data");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key),
Call.REPLACE);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName);
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.REPLACE, stats, Operation.TYPE.MILLI);
final EVCacheLatchImpl latch = new EVCacheLatchImpl(policy == null ? Policy.ALL_MINUS_1 : policy, clients.length - _pool.getWriteOnlyEVCacheClients().length, _appName);
try {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
CachedData cd = null;
int index = 0;
for (EVCacheClient client : clients) {
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (replaceTTLSummary == null) this.replaceTTLSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-ReplaceData-TTL", _appName, null);
if (replaceTTLSummary != null) replaceTTLSummary.record(timeToLive);
if (cd != null) {
if (replaceDataSizeSummary == null) this.replaceDataSizeSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-ReplaceData-Size", _appName, null);
if (replaceDataSizeSummary != null) this.replaceDataSizeSummary.record(cd.getData().length);
}
}
final Future<Boolean> future = client.replace(canonicalKey, cd, timeToLive, latch);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setTTL(timeToLive);
event.setCachedData(cd);
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception setting the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception setting data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("REPLACE : APP " + _appName + ", Took " + op .getDuration() + " milliSec for key : " + canonicalKey);
}
}
@Override
public String getCachePrefix() {
return _cacheName;
}
public String getAppName() {
return _appName;
}
public String getCacheName() {
return _cacheName;
}
public <T> EVCacheLatch appendOrAdd(String key, T value, Transcoder<T> tc, int timeToLive, Policy policy) throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to appendOrAdd the data");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.APPEND_OR_ADD);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.APPEND_OR_ADD, stats, Operation.TYPE.MILLI);
final EVCacheLatchImpl latch = new EVCacheLatchImpl(policy == null ? Policy.ALL_MINUS_1 : policy, clients.length - _pool.getWriteOnlyEVCacheClients().length, _appName);
try {
CachedData cd = null;
for (EVCacheClient client : clients) {
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (cd != null) {
if (appendDataSizeSummary == null) this.appendDataSizeSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-AppendData-Size", _appName, null);
if (appendDataSizeSummary != null) this.appendDataSizeSummary.record(cd.getData().length);
}
}
final Future<Boolean> future = client.appendOrAdd(canonicalKey, cd, timeToLive, latch);
if (log.isDebugEnabled() && shouldLog()) log.debug("APPEND_OR_ADD : APP " + _appName + ", Future " + future + " for key : " + canonicalKey);
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setTTL(timeToLive);
event.setCachedData(cd);
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception while appendOrAdd the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception while appendOrAdd data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("APPEND_OR_ADD : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + canonicalKey);
}
}
@Override
public <T> Future<Boolean>[] appendOrAdd(String key, T value, Transcoder<T> tc, int timeToLive) throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to set the data");
return new EVCacheFuture[0]; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.APPEND_OR_ADD);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheFuture[0];
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.APPEND_OR_ADD, stats, Operation.TYPE.MILLI);
try {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
CachedData cd = null;
int index = 0;
for (EVCacheClient client : clients) {
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (cd != null) {
if (appendDataSizeSummary == null) this.appendDataSizeSummary = EVCacheMetricsFactory.getDistributionSummary(_appName + "-AppendData-Size", _appName, null);
if (appendDataSizeSummary != null) this.appendDataSizeSummary.record(cd.getData().length);
}
}
final Future<Boolean> future = client.append(canonicalKey, cd);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup(), client);
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setCachedData(cd);
event.setTTL(timeToLive);
endEvent(event);
}
for(int i = 0; i < futures.length; i++) {
final EVCacheFuture future = futures[i];
if(!future.get()) {
final EVCacheClient client = future.getEVCacheClient();
if(client != null) {
final Future<Boolean> f = client.add(canonicalKey, timeToLive, cd);
futures[i] = new EVCacheFuture(f, key, _appName, client.getServerGroup(), client);
}
}
}
touchData(canonicalKey, key, timeToLive, clients);
return futures;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception setting the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheFuture[0];
throw new EVCacheException("Exception setting data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("APPEND_OR_ADD : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + canonicalKey);
}
}
public <T> boolean add(String key, T value, Transcoder<T> tc, int timeToLive) throws EVCacheException {
final EVCacheLatch latch = add(key, value, tc, timeToLive, Policy.NONE);
try {
latch.await(_pool.getOperationTimeout().get(), TimeUnit.MILLISECONDS);
return (latch.getSuccessCount() >= latch.getExpectedSuccessCount());
} catch (InterruptedException e) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception adding the data for APP " + _appName + ", key : " + key, e);
final boolean throwExc = doThrowException();
if(throwExc) throw new EVCacheException("Exception add data for APP " + _appName + ", key : " + key, e);
return false;
}
}
@Override
public <T> EVCacheLatch add(String key, T value, Transcoder<T> tc, int timeToLive, Policy policy) throws EVCacheException {
if ((null == key) || (null == value)) throw new IllegalArgumentException();
final boolean throwExc = doThrowException();
final EVCacheClient[] clients = _pool.getEVCacheClientForWrite();
if (clients.length == 0) {
increment("NULL_CLIENT");
if (throwExc) throw new EVCacheException("Could not find a client to Add the data");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key), Call.ADD);
if (event != null) {
try {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
} catch(EVCacheException ex) {
if(throwExc) throw ex;
increment("THROTTLED");
return new EVCacheLatchImpl(policy, 0, _appName); // Fast failure
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.ADD, stats, Operation.TYPE.MILLI);
EVCacheLatch latch = null;
try {
CachedData cd = null;
if (cd == null) {
if (tc != null) {
cd = tc.encode(value);
} else if ( _transcoder != null) {
cd = ((Transcoder<Object>)_transcoder).encode(value);
} else {
cd = _pool.getEVCacheClientForRead().getTranscoder().encode(value);
}
}
if(clientUtil == null) clientUtil = new EVCacheClientUtil(_pool);
latch = clientUtil.add(canonicalKey, cd, timeToLive, policy);
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setTTL(timeToLive);
event.setCachedData(cd);
event.setLatch(latch);
endEvent(event);
}
return latch;
} catch (Exception ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug("Exception adding the data for APP " + _appName + ", key : " + canonicalKey, ex);
if (event != null) eventError(event, ex);
if (!throwExc) return new EVCacheLatchImpl(policy, 0, _appName);
throw new EVCacheException("Exception adding data for APP " + _appName + ", key : " + canonicalKey, ex);
} finally {
op.stop();
if (log.isDebugEnabled() && shouldLog()) log.debug("ADD : APP " + _appName + ", Took " + op.getDuration() + " milliSec for key : " + canonicalKey);
}
}
}