package com.netflix.evcache.operation;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.evcache.EVCacheLatch;
import net.spy.memcached.internal.ListenableFuture;
import net.spy.memcached.internal.OperationCompletionListener;
import net.spy.memcached.internal.OperationFuture;
public class EVCacheLatchImpl implements EVCacheLatch {
private static final Logger log = LoggerFactory.getLogger(EVCacheLatchImpl.class);
private final int expectedSuccessCount;
private final CountDownLatch latch;
private final List<Future<Boolean>> futures;
private final Policy policy;
private final int totalFutureCount;
private final String appName;
public EVCacheLatchImpl(Policy policy, int _count, String appName) {
this.policy = policy;
this.futures = new ArrayList<Future<Boolean>>(_count);
this.appName = appName;
this.totalFutureCount = _count;
this.expectedSuccessCount = policyToCount(policy, _count);
this.latch = new CountDownLatch(expectedSuccessCount);
if (log.isDebugEnabled()) log.debug("Number of Futures = " + _count + "; Number of Futures that need to completed for Latch to be released = " + this.expectedSuccessCount);
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#await(long,
* java.util.concurrent.TimeUnit)
*/
@Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
if (log.isDebugEnabled()) log.debug("Current Latch Count = " + latch.getCount() + "; Will Start the wait");
return latch.await(timeout, unit);
}
/*
* (non-Javadoc)
*
* @see
* com.netflix.evcache.operation.EVCacheLatchI#addFuture(net.spy.memcached.
* internal.ListenableFuture)
*/
public void addFuture(ListenableFuture<Boolean, OperationCompletionListener> future) {
future.addListener(this);
if (future.isDone()) countDown();
this.futures.add(future);
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#isDone()
*/
@Override
public boolean isDone() {
if (latch.getCount() == 0) return true;
return false;
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#countDown()
*/
public void countDown() {
if (log.isDebugEnabled()) log.debug("Current Latch Count = " + latch.getCount() + "; Count Down.");
latch.countDown();
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getPendingCount()
*/
@Override
public int getPendingCount() {
if (log.isDebugEnabled()) log.debug("Pending Count = " + latch.getCount());
return (int) latch.getCount();
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getCompletedCount()
*/
@Override
public int getCompletedCount() {
final int completedCount = (totalFutureCount - (int) latch.getCount());
if (log.isDebugEnabled()) log.debug("Completed Count = " + completedCount);
return completedCount;
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getPendingFutures()
*/
@Override
public List<Future<Boolean>> getPendingFutures() {
final List<Future<Boolean>> returnFutures = new ArrayList<Future<Boolean>>(expectedSuccessCount);
for (Future<Boolean> future : futures) {
if (!future.isDone()) {
returnFutures.add(future);
}
}
return returnFutures;
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getAllFutures()
*/
@Override
public List<Future<Boolean>> getAllFutures() {
return this.futures;
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getCompletedFutures()
*/
@Override
public List<Future<Boolean>> getCompletedFutures() {
final List<Future<Boolean>> returnFutures = new ArrayList<Future<Boolean>>(expectedSuccessCount);
for (Future<Boolean> future : futures) {
if (future.isDone()) {
returnFutures.add(future);
}
}
return returnFutures;
}
private int policyToCount(Policy policy, int count) {
if (policy == null) return 0;
switch (policy) {
case NONE:
return 0;
case ONE:
return 1;
case QUORUM:
if (count == 0)
return 0;
else if (count <= 2)
return 1;
else
return (futures.size() / 2) + 1;
case ALL_MINUS_1:
if (count == 0)
return 0;
else if (count <= 2)
return 1;
else
return count - 1;
default:
return count;
}
}
/*
* (non-Javadoc)
*
* @see
* com.netflix.evcache.operation.EVCacheLatchI#onComplete(net.spy.memcached.
* internal.OperationFuture)
*/
@Override
public void onComplete(OperationFuture<?> future) throws Exception {
if (log.isDebugEnabled()) log.debug("onComplete Callback. Calling Countdown. Completed Future = " + future);
countDown();
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getFailureCount()
*/
@Override
public int getFailureCount() {
int fail = 0;
for (Future<Boolean> future : futures) {
try {
if (future.isDone() && future.get().equals(Boolean.FALSE)) {
fail++;
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
return fail;
}
/*
* (non-Javadoc)
*
* @see
* com.netflix.evcache.operation.EVCacheLatchI#getExpectedSuccessCount()
*/
@Override
public int getExpectedSuccessCount() {
return this.expectedSuccessCount;
}
/*
* (non-Javadoc)
*
* @see com.netflix.evcache.operation.EVCacheLatchI#getSuccessCount()
*/
@Override
public int getSuccessCount() {
int success = 0;
for (Future<Boolean> future : futures) {
try {
if (future.isDone() && future.get().equals(Boolean.TRUE)) {
success++;
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
return success;
}
public String getAppName() {
return appName;
}
public Policy getPolicy() {
return this.policy;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{\"AppName\":\"");
builder.append(getAppName());
builder.append("\",\"isDone\":\"");
builder.append(isDone());
builder.append("\",\"Pending Count\":\"");
builder.append(getPendingCount());
builder.append("\",\"Completed Count\":\"");
builder.append(getCompletedCount());
builder.append("\",\"Pending Futures\":\"");
builder.append(getPendingFutures());
builder.append("\",\"All Futures\":\"");
builder.append(getAllFutures());
builder.append("\",\"Completed Futures\":\"");
builder.append(getCompletedFutures());
builder.append("\",\"Failure Count\":\"");
builder.append(getFailureCount());
builder.append("\",\"Success Count\":\"");
builder.append(getSuccessCount());
builder.append("\",\"Excpected Success Count\":\"");
builder.append(getExpectedSuccessCount());
builder.append("\"}");
return builder.toString();
}
@Override
public int getPendingFutureCount() {
int count = 0;
for (Future<Boolean> future : futures) {
if (!future.isDone()) {
count++;
}
}
return count;
}
@Override
public int getCompletedFutureCount() {
int count = 0;
for (Future<Boolean> future : futures) {
if (future.isDone()) {
count++;
}
}
return count;
}
}