
com.netflix.evcache.operation.EVCacheBulkGetFuture Maven / Gradle / Ivy
The newest version!
package com.netflix.evcache.operation;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReferenceArray;
import com.netflix.evcache.EVCacheGetOperationListener;
import com.netflix.evcache.util.Pair;
import net.spy.memcached.internal.BulkGetCompletionListener;
import net.spy.memcached.internal.CheckedOperationTimeoutException;
import net.spy.memcached.ops.GetOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.evcache.metrics.EVCacheMetricsFactory;
import com.netflix.evcache.pool.EVCacheClient;
import com.netflix.evcache.pool.ServerGroup;
import com.netflix.evcache.util.EVCacheConfig;
import com.netflix.spectator.api.BasicTag;
import com.netflix.spectator.api.Tag;
import com.sun.management.GcInfo;
import net.spy.memcached.MemcachedConnection;
import net.spy.memcached.internal.BulkGetFuture;
import net.spy.memcached.ops.Operation;
import net.spy.memcached.ops.OperationState;
import rx.Scheduler;
import rx.Single;
/**
* Future for handling results from bulk gets.
*
* Not intended for general use.
*
* types of objects returned from the GETBULK
*/
@SuppressWarnings("restriction")
public class EVCacheBulkGetFuture extends BulkGetFuture {
private static final Logger log = LoggerFactory.getLogger(EVCacheBulkGetFuture.class);
private final Map> rvMap;
private final Collection ops;
private final CountDownLatch latch;
private final long start;
private final EVCacheClient client;
private AtomicReferenceArray operationStates;
public EVCacheBulkGetFuture(Map> m, Collection getOps, CountDownLatch l, ExecutorService service, EVCacheClient client) {
super(m, getOps, l, service);
rvMap = m;
ops = getOps;
latch = l;
this.start = System.currentTimeMillis();
this.client = client;
this.operationStates = null;
}
public Map getSome(long to, TimeUnit unit, boolean throwException, boolean hasZF)
throws InterruptedException, ExecutionException {
assert operationStates != null;
boolean allCompleted = latch.await(to, unit);
if(log.isDebugEnabled()) log.debug("Took " + (System.currentTimeMillis() - start)+ " to fetch " + rvMap.size() + " keys from " + client);
long pauseDuration = -1;
List tagList = null;
String statusString = EVCacheMetricsFactory.SUCCESS;
try {
if (!allCompleted) {
boolean gcPause = false;
tagList = new ArrayList(7);
tagList.addAll(client.getTagList());
tagList.add(new BasicTag(EVCacheMetricsFactory.CALL_TAG, EVCacheMetricsFactory.BULK_OPERATION));
final RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
final long vmStartTime = runtimeBean.getStartTime();
final List gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcMXBean : gcMXBeans) {
if (gcMXBean instanceof com.sun.management.GarbageCollectorMXBean) {
final GcInfo lastGcInfo = ((com.sun.management.GarbageCollectorMXBean) gcMXBean).getLastGcInfo();
// If no GCs, there was no pause.
if (lastGcInfo == null) {
continue;
}
final long gcStartTime = lastGcInfo.getStartTime() + vmStartTime;
if (gcStartTime > start) {
gcPause = true;
if (log.isDebugEnabled()) log.debug("Total duration due to gc event = " + lastGcInfo.getDuration() + " msec.");
break;
}
}
}
// redo the same op once more since there was a chance of gc pause
if (gcPause) {
allCompleted = latch.await(to, unit);
tagList.add(new BasicTag(EVCacheMetricsFactory.PAUSE_REASON, EVCacheMetricsFactory.GC));
if (log.isDebugEnabled()) log.debug("Retry status : " + allCompleted);
if (allCompleted) {
tagList.add(new BasicTag(EVCacheMetricsFactory.FETCH_AFTER_PAUSE, EVCacheMetricsFactory.YES));
} else {
tagList.add(new BasicTag(EVCacheMetricsFactory.FETCH_AFTER_PAUSE, EVCacheMetricsFactory.NO));
}
} else {
tagList.add(new BasicTag(EVCacheMetricsFactory.PAUSE_REASON, EVCacheMetricsFactory.SCHEDULE));
}
pauseDuration = System.currentTimeMillis() - start;
if (log.isDebugEnabled()) log.debug("Total duration due to gc event = " + (System.currentTimeMillis() - start) + " msec.");
}
boolean hadTimedoutOp = false;
for (int i = 0; i < operationStates.length(); i++) {
SingleOperationState state = operationStates.get(i);
if (!state.completed && !allCompleted) {
MemcachedConnection.opTimedOut(state.op);
hadTimedoutOp = true;
} else {
MemcachedConnection.opSucceeded(state.op);
}
}
if (!allCompleted && !hasZF && hadTimedoutOp) statusString = EVCacheMetricsFactory.TIMEOUT;
for (int i = 0; i < operationStates.length(); i++) {
SingleOperationState state = operationStates.get(i);
if (state.cancelled) {
if (hasZF) statusString = EVCacheMetricsFactory.CANCELLED;
if (throwException) throw new ExecutionException(new CancellationException("Cancelled"));
}
if (state.errored && throwException) throw new ExecutionException(state.op.getException());
}
Map m = new HashMap();
for (Map.Entry> me : rvMap.entrySet()) {
m.put(me.getKey(), me.getValue().get());
}
return m;
} finally {
if(pauseDuration > 0) {
tagList.add(new BasicTag(EVCacheMetricsFactory.OPERATION_STATUS, statusString));
EVCacheMetricsFactory.getInstance().getPercentileTimer(EVCacheMetricsFactory.INTERNAL_PAUSE, tagList, Duration.ofMillis(EVCacheConfig.getInstance().getPropertyRepository().get(getApp() + ".max.read.duration.metric", Integer.class)
.orElseGet("evcache.max.read.duration.metric").orElse(20).get().intValue())).record(pauseDuration, TimeUnit.MILLISECONDS);
}
}
}
public void setExpectedCount(int size) {
assert operationStates == null;
operationStates = new AtomicReferenceArray<>(size);
}
// a lot of hoops we go through to avoid hitting the lock
static class SingleOperationState {
final Operation op;
final boolean completed;
final boolean cancelled;
final boolean errored;
final boolean timedOut;
public SingleOperationState(Operation op) {
this.op = op;
this.completed = op.getState() == OperationState.COMPLETE;
this.cancelled = op.isCancelled();
this.errored = op.hasErrored();
this.timedOut = op.isTimedOut();
}
}
public CompletableFuture
© 2015 - 2025 Weber Informatics LLC | Privacy Policy