Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.netflix.evcache.EVCacheImpl Maven / Gradle / Ivy
package com.netflix.evcache;
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.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.config.ChainedDynamicProperty;
import com.netflix.config.DynamicBooleanProperty;
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.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 rx.Observable;
import rx.Scheduler;
import rx.Single;
import static com.netflix.evcache.util.Sneaky.sneakyThrow;
/**
* 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 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 = (_cacheName == null) ? _appName : _appName + "-" + _cacheName + "-";
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) {
if (this._cacheName == null) return key;
return _cacheName + ':' + key;
}
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 getEVCacheEventListeners() {
return _poolManager.getEVCacheEventListeners();
}
private EVCacheEvent createEVCacheEvent(Collection clients, Collection keys, Call call) {
final List 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) {
for (EVCacheEventListener evcacheEventListener : getEVCacheEventListeners()) {
if (evcacheEventListener.onThrottle(event)) {
return true;
}
}
return false;
}
private void startEvent(EVCacheEvent event) {
final List evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onStart(event);
}
}
private void endEvent(EVCacheEvent event) {
final List evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onComplete(event);
}
}
private void eventError(EVCacheEvent event, Throwable t) {
final List evcacheEventListenerList = getEVCacheEventListeners();
for (EVCacheEventListener evcacheEventListener : evcacheEventListenerList) {
evcacheEventListener.onError(event, t);
}
}
private EVCacheInMemoryCache getInMemoryCache() {
if (cache == null) cache = new EVCacheInMemoryCache(_appName);
return (EVCacheInMemoryCache) cache;
}
public T get(String key) throws EVCacheException {
return this.get(key, (Transcoder) _transcoder);
}
private void increment(String metric) {
increment(null, _metricPrefix + metric);
}
private void increment(String serverGroup, String metric) {
EVCacheMetricsFactory.increment(_appName, _cacheName, serverGroup, _metricPrefix + metric);
}
public T get(String key, Transcoder tc) throws EVCacheException {
if (null == key) throw new IllegalArgumentException("Key cannot be null");
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(key),
Call.GET);
if (event != null) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
T value = (T) getInMemoryCache().get(canonicalKey);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value retrieved from inmemory cache for APP " + _appName
+ ", key : " + canonicalKey + "; value : " + value);
if (value != null) return value;
}
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 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;
data = getData(fbClient, canonicalKey, tc, throwEx, false);
if (log.isDebugEnabled() && shouldLog()) log.debug("Retry for APP " + _appName + ", key [" + canonicalKey + "], Value [" + data + "]" + ", ServerGroup : " + fbClient.getServerGroup());
if (data != null) {
client = fbClient;
break;
}
}
increment(client.getServerGroupName(), "RETRY_" + ((data == null) ? "MISS" : "HIT"));
}
}
if (data != null) {
stats.cacheHit(Call.GET);
if (event != null) event.setAttribute("status", "GHIT");
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, data);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value added to inmemory cache for APP " + _appName + ", key : " + canonicalKey);
}
} 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 + "], 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=", 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 Single get(String key, Scheduler scheduler) {
return this.get(key, (Transcoder) _transcoder, scheduler);
}
public Single get(String key, Transcoder 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
return Single.error(new EVCacheException("Request Throttled for app " + _appName + " & key " + key));
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
T value = (T) getInMemoryCache().get(canonicalKey);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value retrieved from inmemory cache for APP " + _appName + ", key : " + canonicalKey + "; value : " + value);
if (value != null) return Single.just(value);
}
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 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(), "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");
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, data);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value added to inmemory cache for APP " + _appName + ", key : " + canonicalKey);
}
} 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 + "], 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=", 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 getData(EVCacheClient client, String canonicalKey, Transcoder tc, boolean throwException, boolean hasZF) throws Exception {
if (client == null) return null;
try {
if(tc == null && _transcoder != null) tc = (Transcoder)_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 Single getData(int index, int size, EVCacheClient client, String canonicalKey, Transcoder tc, boolean throwEx, boolean throwExc, boolean hasZF, Scheduler scheduler) {
if(index >= size -1) throwEx = throwExc;
return getData(client, canonicalKey, tc, throwEx, hasZF, scheduler);
}
private Single getData(EVCacheClient client, String canonicalKey, Transcoder 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)_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);
}
});
}
private T getAndTouchData(EVCacheClient client, String canonicalKey, Transcoder tc, boolean throwException,
boolean hasZF, int timeToLive) throws Exception {
try {
if(tc == null && _transcoder != null) tc = (Transcoder)_transcoder;
return client.getAndTouch(canonicalKey, tc, timeToLive, throwException, hasZF);
} catch (EVCacheReadQueueException ex) {
if (log.isDebugEnabled() && shouldLog()) log.debug(
"EVCacheReadQueueException while getAndTouch 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 getAndTouch 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 getAndTouch data for APP " + _appName
+ ", key : " + canonicalKey, ex);
if (!throwException || hasZF) return null;
throw ex;
}
}
public T getAndTouch(String key, int timeToLive) throws EVCacheException {
return this.getAndTouch(key, timeToLive, (Transcoder) _transcoder);
}
public Single getAndTouch(String key, int timeToLive, Scheduler scheduler) {
return this.getAndTouch(key, timeToLive, (Transcoder) _transcoder, scheduler);
}
public Single getAndTouch(String key, int timeToLive, Transcoder 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
return Single.error(new EVCacheException("Request Throttled for app " + _appName + " & key " + key));
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
T value = (T) getInMemoryCache().get(canonicalKey);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value retrieved from inmemory cache for APP " + _appName + ", key : " + canonicalKey + "; value : " + value);
if (value != null) {
try {
touch(key, timeToLive);
} catch (EVCacheException e) {
throw sneakyThrow(new EVCacheException("Exception performing touch for APP " + _appName + ", key = " + canonicalKey, e));
}
return Single.just(value);
}
}
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 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(), "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");
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, data);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value added to inmemory cache for APP " + _appName + ", key : " + canonicalKey);
}
// touch all copies
try {
touch(key, timeToLive);
} catch (EVCacheException 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 + "], 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=", 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 getAndTouch(String key, int timeToLive, Transcoder tc) throws EVCacheException {
if (null == key) throw new IllegalArgumentException("Key cannot be null");
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(key), Call.GET_AND_TOUCH);
if (event != null) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return null;
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
T value = (T) getInMemoryCache().get(canonicalKey);
if (value != null) {
touch(key, timeToLive);
return value;
}
}
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);
if (data == null && hasZF) {
final List 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;
data = getAndTouchData(fbClient, canonicalKey, tc, throwEx, false, timeToLive);
if (log.isDebugEnabled() && shouldLog()) log.debug("GetAndTouch Retry for APP " + _appName + ", key [" + canonicalKey + "], Value [" + data + "]" + ", ServerGroup : " + fbClient.getServerGroup());
if (data != null) {
client = fbClient;
break;
}
}
increment(client.getServerGroupName(), "RETRY_" + ((data == null) ? "MISS" : "HIT"));
}
if (data != null) {
stats.cacheHit(Call.GET_AND_TOUCH);
if (event != null) event.setAttribute("status", "THIT");
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, data);
if (log.isDebugEnabled() && shouldLog()) log.debug("Value added to inmemory cache for APP " + _appName + ", key : " + canonicalKey);
}
// touch all copies
touch(key, timeToLive);
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", key [" + canonicalKey + "], 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=", 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);
}
}
public Future[] touch(String key, int timeToLive) 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 EVCacheFuture[0]; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key),
Call.TOUCH);
if (event != null) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheFuture[0];
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
try {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
int index = 0;
for (EVCacheClient client : clients) {
final Future future = client.touch(canonicalKey, timeToLive);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
}
if (touchTTLSummary == null) this.touchTTLSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-TouchData-TTL");
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);
endEvent(event);
}
return futures;
} 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 EVCacheFuture[0];
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);
}
}
public Future getAsynchronous(String key) throws EVCacheException {
return this.getAsynchronous(key, (Transcoder) _transcoder);
};
@Override
public Future getAsynchronous(String key, Transcoder 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return null;
}
startEvent(event);
}
final Future 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)_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 Map getBulkData(EVCacheClient client, Collection canonicalKeys, Transcoder tc,
boolean throwException, boolean hasZF) throws Exception {
try {
if(tc == null && _transcoder != null) tc = (Transcoder)_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 Map getBulk(Collection keys, Transcoder tc) throws EVCacheException {
return getBulk(keys, tc, false, 0);
}
public Map getBulkAndTouch(Collection keys, Transcoder tc, int timeToLive)
throws EVCacheException {
return getBulk(keys, tc, true, timeToLive);
}
private Map getBulk(Collection keys, Transcoder tc, boolean touch, int ttl)
throws EVCacheException {
if (null == keys) throw new IllegalArgumentException();
if (keys.isEmpty()) return Collections. 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. emptyMap();// Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), keys, Call.BULK);
if (event != null) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & keys " + keys);
return Collections. emptyMap();
}
startEvent(event);
}
final Collection canonicalKeys = new ArrayList();
/* 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(), "BULK_GET");
Map retMap = getBulkData(client, canonicalKeys, tc, throwEx, hasZF);
List 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;
retMap = getBulkData(fbClient, canonicalKeys, tc, throwEx, false);
if (log.isDebugEnabled() && shouldLog()) log.debug("Fallback for APP " + _appName + ", key [" + canonicalKeys + "], Value [" + retMap + "]" + ", zone : " + fbClient.getZone());
if (retMap != null && !retMap.isEmpty()) break;
}
increment(client.getServerGroupName(), "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 retryKeys = new ArrayList(initRetrySize);
for (Iterator 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);
final Map 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 i : fbRetMap.entrySet()) {
retMap.put(i.getKey(), i.getValue());
if (log.isDebugEnabled() && shouldLog()) log.debug("Fallback for APP " + _appName
+ ", key [" + i.getKey() + "], Value [" + i.getValue() + "]");
}
if (retryKeys.size() == fbRetMap.size()) break;
if (ind < fbClients.size()) {
retryKeys = new ArrayList(keys.size() - retMap.size());
for (Iterator keysItr = canonicalKeys.iterator(); keysItr.hasNext();) {
final String key = keysItr.next();
if (!retMap.containsKey(key)) {
retryKeys.add(key);
}
}
}
}
if (retMap.size() > initRetMapSize) increment(client.getServerGroupName(), "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();
for (String k : keys) {
retMap.put(k, null);
}
}
stats.cacheMiss(Call.BULK);
/* If both Retry and first request fail Exit Immediately. */
increment(client.getServerGroupName(), "BULK_MISS");
if (event != null) endEvent(event);
return retMap;
}
/* Decanonicalize the keys */
final Map decanonicalR = new HashMap((canonicalKeys.size() * 4) / 3 + 1);
for (Iterator 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) touch(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(), "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(), "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=", 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 Map getBulk(Collection keys) throws EVCacheException {
return (this.getBulk(keys, (Transcoder) _transcoder));
}
public Map getBulk(String... keys) throws EVCacheException {
return (this.getBulk(Arrays.asList(keys), (Transcoder) _transcoder));
}
public Map getBulk(Transcoder tc, String... keys) throws EVCacheException {
return (this.getBulk(Arrays.asList(keys), tc));
}
@Override
public EVCacheFuture[] set(String key, T value, Transcoder tc, int timeToLive) throws EVCacheException {
final EVCacheLatch latch = this.set(key, value, tc, timeToLive, null);
if (latch == null) return new EVCacheFuture[0];
final List> 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 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) futures.get(
i)).getServerGroup());
} else {
eFutures[i] = new EVCacheFuture(futures.get(i), key, _appName, null);
}
}
return eFutures;
}
public EVCacheLatch set(String key, T value, Policy policy) throws EVCacheException {
return set(key, value, (Transcoder)_transcoder, _timeToLive, policy);
}
public EVCacheLatch set(String key, T value, int timeToLive, Policy policy) throws EVCacheException {
return set(key, value, (Transcoder)_transcoder, timeToLive, policy);
}
public EVCacheLatch set(String key, T value, Transcoder tc, EVCacheLatch.Policy policy) throws EVCacheException {
return set(key, value, tc, _timeToLive, policy);
}
public EVCacheLatch set(String key, T value, Transcoder 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName);
}
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, _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)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (setTTLSummary == null) this.setTTLSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-SetData-TTL");
if (setTTLSummary != null) setTTLSummary.record(timeToLive);
if (cd != null) {
if (setDataSizeSummary == null) this.setDataSizeSummary = EVCacheConfig.getInstance()
.getDistributionSummary(_appName + "-SetData-Size");
if (setDataSizeSummary != null) this.setDataSizeSummary.record(cd.getData().length);
}
}
final Future future = client.set(canonicalKey, cd, timeToLive, latch);
if (log.isDebugEnabled() && shouldLog()) log.debug("SET : APP " + _appName + ", Future " + future
+ " for key : " + canonicalKey);
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, value);
}
}
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 EVCacheFuture[] append(String key, T value) throws EVCacheException {
return this.append(key, value, null);
}
public EVCacheFuture[] append(String key, T value, Transcoder tc) 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheFuture[0];
}
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)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
}
final Future future = client.append(canonicalKey, cd);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
if (cd != null) {
if (appendDataSizeSummary == null) this.appendDataSizeSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-AppendData-Size");
if (appendDataSizeSummary != null) this.appendDataSizeSummary.record(cd.getData().length);
}
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
event.setCachedData(cd);
endEvent(event);
}
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 EVCacheFuture[] set(String key, T value, Transcoder tc) throws EVCacheException {
return this.set(key, value, tc, _timeToLive);
}
public EVCacheFuture[] set(String key, T value, int timeToLive) throws EVCacheException {
return this.set(key, value, (Transcoder) _transcoder, timeToLive);
}
public EVCacheFuture[] set(String key, T value) throws EVCacheException {
return this.set(key, value, (Transcoder) _transcoder, _timeToLive);
}
public EVCacheFuture[] delete(String key) 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 EVCacheFuture[0]; // Fast failure
}
final EVCacheEvent event = createEVCacheEvent(Arrays.asList(clients), Collections.singletonList(key),
Call.DELETE);
if (event != null) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheFuture[0];
}
startEvent(event);
}
final String canonicalKey = getCanonicalizedKey(key);
if (_useInMemoryCache.get()) {
getInMemoryCache().delete(canonicalKey);
}
final Operation op = EVCacheMetricsFactory.getOperation(_metricName, Call.DELETE, stats);
try {
final EVCacheFuture[] futures = new EVCacheFuture[clients.length];
for (int i = 0; i < clients.length; i++) {
Future future = clients[i].delete(canonicalKey);
futures[i] = new EVCacheFuture(future, key, _appName, clients[i].getServerGroup());
}
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
endEvent(event);
}
return futures;
} 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 EVCacheFuture[0];
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 String getAppName() {
return _appName;
}
public String getCacheName() {
return _cacheName;
}
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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
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 EVCacheLatch replace(String key, T value, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder) _transcoder, policy);
}
@Override
public EVCacheLatch replace(String key, T value, Transcoder tc, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder) _transcoder, _timeToLive, policy);
}
public EVCacheLatch replace(String key, T value, int timeToLive, Policy policy) throws EVCacheException {
return replace(key, value, (Transcoder)_transcoder, timeToLive, policy);
}
@Override
public EVCacheLatch replace(String key, T value, Transcoder 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) {
if (shouldThrottle(event)) {
increment("THROTTLED");
if (throwExc) throw new EVCacheException("Request Throttled for app " + _appName + " & key " + key);
return new EVCacheLatchImpl(policy, 0, _appName);
}
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, _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)_transcoder).encode(value);
} else {
cd = client.getTranscoder().encode(value);
}
if (replaceTTLSummary == null) this.replaceTTLSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-ReplaceData-TTL");
if (replaceTTLSummary != null) replaceTTLSummary.record(timeToLive);
if (cd != null) {
if (replaceDataSizeSummary == null) this.replaceDataSizeSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-ReplaceData-Size");
if (replaceDataSizeSummary != null) this.replaceDataSizeSummary.record(cd.getData().length);
}
}
final Future future = client.replace(canonicalKey, cd, timeToLive, latch);
futures[index++] = new EVCacheFuture(future, key, _appName, client.getServerGroup());
if (_useInMemoryCache.get()) {
getInMemoryCache().put(canonicalKey, value);
}
}
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);
}
}
}