package voldemort.store.routed;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.ObjectName;
import voldemort.annotations.jmx.JmxGetter;
import voldemort.store.InsufficientOperationalNodesException;
import voldemort.store.InsufficientZoneResponsesException;
import voldemort.store.InvalidMetadataException;
import voldemort.store.StoreTimeoutException;
import voldemort.store.UnreachableStoreException;
import voldemort.store.quota.QuotaExceededException;
import voldemort.utils.JmxUtils;
import voldemort.utils.Utils;
import voldemort.versioning.ObsoleteVersionException;
/**
* Tracks all the exceptions we see, down at the routing layer also including
* ones that will be eventually propagated up to the client from the routing
* layer
*
*/
public class PipelineRoutedStats {
protected final ConcurrentHashMap<Class<? extends Exception>, AtomicLong> errCountMap;
protected final AtomicLong severeExceptionCount;
protected final AtomicLong benignExceptionCount;
protected final String name;
private boolean isRegistered = false;
private ObjectName jmxObjectName = null;
private int referenceCount;
protected PipelineRoutedStats(String name) {
this.name = Utils.notNull(name);
errCountMap = new ConcurrentHashMap<Class<? extends Exception>, AtomicLong>();
errCountMap.put(InvalidMetadataException.class, new AtomicLong(0));
errCountMap.put(InsufficientOperationalNodesException.class, new AtomicLong(0));
errCountMap.put(InsufficientZoneResponsesException.class, new AtomicLong(0));
errCountMap.put(UnreachableStoreException.class, new AtomicLong(0));
errCountMap.put(StoreTimeoutException.class, new AtomicLong(0));
errCountMap.put(ObsoleteVersionException.class, new AtomicLong(0));
errCountMap.put(QuotaExceededException.class, new AtomicLong(0));
severeExceptionCount = new AtomicLong(0);
benignExceptionCount = new AtomicLong(0);
referenceCount = 0;
}
@JmxGetter(name = "numSevereExceptions", description = "Number of exceptions considered serious errors")
public long getNumSevereExceptions() {
return severeExceptionCount.get();
}
@JmxGetter(name = "numBenignExceptions", description = "Number of exceptions considered benign")
public long getNumBenignExceptions() {
return benignExceptionCount.get();
}
@JmxGetter(name = "numInsufficientOperationalNodesExceptions", description = "Number of client operations failed due to sufficient nodes not being up")
public long getNumInsufficientOperationalNodesExceptions() {
return errCountMap.get(InsufficientOperationalNodesException.class).get();
}
@JmxGetter(name = "numInsufficientZoneResponsesExceptions", description = "Number of client operations failed due to sufficient nodes not up across zones")
public long getNumInsufficientZoneResponsesExceptions() {
return errCountMap.get(InsufficientZoneResponsesException.class).get();
}
@JmxGetter(name = "numInvalidMetadataExceptions", description = "Number of times the metadata was invalid at the client")
public long getNumInvalidMetadataExceptions() {
return errCountMap.get(InvalidMetadataException.class).get();
}
@JmxGetter(name = "numUnreachableStoreExceptions", description = "Number of requests incomplete since some server could not be reached")
public long getNumUnreachableStoreExceptions() {
return errCountMap.get(UnreachableStoreException.class).get();
}
@JmxGetter(name = "numStoreTimeoutExceptions", description = "Number of requests timed out since some server was overloaded/unavailable")
public long getNumStoreTimeoutExceptions() {
return errCountMap.get(StoreTimeoutException.class).get();
}
@JmxGetter(name = "numObsoleteVersionExceptions", description = "Number of requests that got a ObsoleteVersionException as response")
public long getNumObsoleteVersionExceptions() {
return errCountMap.get(ObsoleteVersionException.class).get();
}
@JmxGetter(name = "numQuotaExceededExceptions", description = "Number of client operations failed due to exceeding quota")
public long getNumQuotaExceededExceptions() {
return errCountMap.get(QuotaExceededException.class).get();
}
@JmxGetter(name = "getExceptionCountsAsString", description = "Returns counts of all the Exceptions seen so far as a string")
public String getExceptionCountsAsString() {
StringBuilder result = new StringBuilder();
Iterator<Entry<Class<? extends Exception>, AtomicLong>> itr = errCountMap.entrySet()
.iterator();
while(itr.hasNext()) {
Entry<Class<? extends Exception>, AtomicLong> pair = itr.next();
result.append(pair.getKey().getName() + ":" + pair.getValue().get() + "\n");
}
return result.toString();
}
public void reportException(Exception e) {
if(isSevere(e))
severeExceptionCount.incrementAndGet();
else
benignExceptionCount.incrementAndGet();
errCountMap.putIfAbsent(e.getClass(), new AtomicLong(0));
errCountMap.get(e.getClass()).incrementAndGet();
}
public boolean isSevere(Exception ve) {
if(ve instanceof InsufficientOperationalNodesException
|| ve instanceof InsufficientZoneResponsesException
|| ve instanceof InvalidMetadataException)
return true;
else
return false;
}
/**
* Multiple {@link PipelineRoutedStore} share the same
* {@link PipelineRoutedStats} object IF Mbean is not registered, the first
* caller will register it. All callers increment the counter.
*/
public synchronized void registerJmxIfRequired() {
referenceCount++;
if(isRegistered == false) {
String domain = JmxUtils.getPackageName(this.getClass());
this.jmxObjectName = JmxUtils.createObjectName(domain, this.name);
JmxUtils.registerMbean(this, this.jmxObjectName);
isRegistered = true;
}
}
/**
* Last caller of this method will unregister the Mbean. All callers
* decrement the counter.
*/
public synchronized void unregisterJmxIfRequired() {
referenceCount--;
if (isRegistered == true && referenceCount <= 0) {
JmxUtils.unregisterMbean(this.jmxObjectName);
isRegistered = false;
}
}
}