in evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java [1444:1531]
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"));
checkTTL(timeToLive, Call.GET_AND_TOUCH);
if(hashKey.get()) {
return Single.error(new IllegalArgumentException("Not supported"));
}
final boolean throwExc = doThrowException();
final EVCacheClient client = _pool.getEVCacheClientForRead();
if (client == null) {
incrementFastFail(EVCacheMetricsFactory.NULL_CLIENT, Call.GET_AND_TOUCH);
return Single.error(new EVCacheException("Could not find a client to get and touch the data for APP " + _appName));
}
final EVCacheKey evcKey = getEVCacheKey(key);
final EVCacheEvent event = createEVCacheEvent(Collections.singletonList(client), Call.GET_AND_TOUCH);
if (event != null) {
event.setEVCacheKeys(Arrays.asList(evcKey));
if (shouldThrottle(event)) {
incrementFastFail(EVCacheMetricsFactory.THROTTLED, Call.GET_AND_TOUCH);
return Single.error(new EVCacheException("Request Throttled for app " + _appName + " & key " + key));
}
event.setTTL(timeToLive);
startEvent(event);
}
final long start = EVCacheMetricsFactory.getInstance().getRegistry().clock().wallTime();
final boolean hasZF = hasZoneFallback();
final boolean throwEx = hasZF ? false : throwExc;
//anyway we have to touch all copies so let's just reuse getData instead of getAndTouch
return getData(client, evcKey, 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(
//TODO : for the last one make sure to pass throwExc
fbClient -> getData(fbClients.indexOf(fbClient), fbClients.size(), fbClient, evcKey, tc, throwEx, throwExc, false, scheduler)
.doOnSuccess(fbData -> {
//increment("RETRY_" + ((fbData == null) ? "MISS" : "HIT"));
})
.toObservable()))
.firstOrDefault(null, fbData -> (fbData != null)).toSingle();
}
}
return Single.just(data);
}).map(data -> {
//increment("GetCall");
if (data != null) {
//increment("GetHit");
if (event != null) event.setAttribute("status", "THIT");
// touch all copies
try {
touchData(evcKey, timeToLive);
} catch (Exception e) {
throw sneakyThrow(new EVCacheException("Exception performing touch for APP " + _appName + ", key = " + evcKey, e));
}
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", key [" + evcKey + (log.isTraceEnabled() ? "], Value [" + data : "") + "], ServerGroup : " + client .getServerGroup());
} else {
//increment("GetMiss");
if (event != null) event.setAttribute("status", "TMISS");
if (log.isInfoEnabled() && shouldLog()) log.info("GET_AND_TOUCH : APP " + _appName + " ; cache miss for key : " + evcKey);
}
if (event != null) endEvent(event);
return data;
}).onErrorReturn(ex -> {
if (ex instanceof net.spy.memcached.internal.CheckedOperationTimeoutException) {
if (event != null) {
event.setStatus(EVCacheMetricsFactory.TIMEOUT);
eventError(event, ex);
}
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("CheckedOperationTimeoutException executing getAndTouch APP " + _appName + ", key = " + evcKey
+ ".\nYou can set the following property to increase the timeout " + _appName + ".EVCacheClientPool.readTimeout=<timeout in milli-seconds>", ex));
} else {
if (event != null) {
event.setStatus(EVCacheMetricsFactory.ERROR);
eventError(event, ex);
}
if (event != null) eventError(event, ex);
if (!throwExc) return null;
throw sneakyThrow(new EVCacheException("Exception executing getAndTouch APP " + _appName + ", key = " + evcKey, ex));
}
}).doAfterTerminate(() -> {
final long duration = EVCacheMetricsFactory.getInstance().getRegistry().clock().wallTime()- start;
getTimer(Call.GET_AND_TOUCH.name(), EVCacheMetricsFactory.READ, null, EVCacheMetricsFactory.SUCCESS, 1, maxReadDuration.get().intValue(),client.getServerGroup()).record(duration, TimeUnit.MILLISECONDS);
if (log.isDebugEnabled() && shouldLog()) log.debug("GET_AND_TOUCH : APP " + _appName + ", Took " + duration+ " milliSec.");
});
}