package org.infinispan.stats.topK;
import java.util.Map;
import org.infinispan.commands.VisitableCommand;
import org.infinispan.commands.read.GetAllCommand;
import org.infinispan.commands.read.GetKeyValueCommand;
import org.infinispan.commands.tx.PrepareCommand;
import org.infinispan.commands.write.PutKeyValueCommand;
import org.infinispan.context.InvocationContext;
import org.infinispan.context.impl.TxInvocationContext;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.factories.ComponentRegistry;
import org.infinispan.interceptors.BaseCustomAsyncInterceptor;
import org.infinispan.interceptors.InvocationFinallyAction;
import org.infinispan.jmx.annotations.MBean;
import org.infinispan.jmx.annotations.ManagedAttribute;
import org.infinispan.jmx.annotations.ManagedOperation;
import org.infinispan.jmx.annotations.Parameter;
import org.infinispan.stats.logging.Log;
import org.infinispan.stats.wrappers.TopKeyLockManager;
import org.infinispan.transaction.WriteSkewException;
import org.infinispan.util.concurrent.locks.LockManager;
import org.infinispan.util.logging.LogFactory;
/**
* Intercepts the VisitableCommands to calculate the corresponding top-key values.
*
* @author Pedro Ruivo
* @since 6.0
*/
@MBean(objectName = "CacheUsageStatistics", description = "Keeps tracks of the accessed keys")
public class CacheUsageInterceptor extends BaseCustomAsyncInterceptor {
public static final int DEFAULT_TOP_KEY = 10;
private static final Log log = LogFactory.getLog(CacheUsageInterceptor.class, Log.class);
private StreamSummaryContainer streamSummaryContainer;
private DistributionManager distributionManager;
private final InvocationFinallyAction writeSkewReturnHandler = new InvocationFinallyAction() {
@Override
public void accept(InvocationContext rCtx, VisitableCommand rCommand, Object rv,
Throwable throwable) throws Throwable {
if (throwable instanceof WriteSkewException) {
WriteSkewException wse = (WriteSkewException) throwable;
Object key = wse.getKey();
if (streamSummaryContainer.isEnabled() && key != null && rCtx.isOriginLocal()) {
streamSummaryContainer.addWriteSkewFailed(key);
}
throw wse;
}
}
};
@Override
public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable {
if (streamSummaryContainer.isEnabled() && ctx.isOriginLocal()) {
streamSummaryContainer.addGet(command.getKey(), isRemote(command.getKey()));
}
return invokeNext(ctx, command);
}
@Override
public Object visitGetAllCommand(InvocationContext ctx, GetAllCommand command) throws Throwable {
if (streamSummaryContainer.isEnabled() && ctx.isOriginLocal()) {
for (Object key : command.getKeys()) {
streamSummaryContainer.addGet(key, isRemote(key));
}
}
return invokeNext(ctx, command);
}
// TODO: implement visitPutMapCommand
@Override
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
if (streamSummaryContainer.isEnabled() && ctx.isOriginLocal()) {
streamSummaryContainer.addPut(command.getKey(), isRemote(command.getKey()));
}
return invokeNextAndFinally(ctx, command, writeSkewReturnHandler);
}
@Override
public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable {
return invokeNextAndFinally(ctx, command, writeSkewReturnHandler);
}
@ManagedOperation(description = "Resets statistics gathered by this component",
displayName = "Reset Statistics (Statistics)")
public void resetStatistics() {
streamSummaryContainer.resetAll();
}
@ManagedOperation(description = "Set K for the top-K values",
displayName = "Set capacity")
public void setTopKValue(@Parameter(name = "n", description = "the n-th top key to collect") int n) {
streamSummaryContainer.setCapacity(n);
}
@ManagedAttribute(description = "Shows the current capacity for top-K values",
displayName = "getCapacity")
public int getCapacity() {
return streamSummaryContainer.getCapacity();
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most read remotely by this instance",
displayName = "Top Remote Read Keys")
public Map<String, Long> getRemoteTopGets() {
return getNRemoteTopGets(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most read remotely by this instance",
displayName = "Nth Top Remote Read Keys")
public Map<String, Long> getNRemoteTopGets(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.REMOTE_GET, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most read locally by this instance",
displayName = "Top Local Read Keys")
public Map<String, Long> getLocalTopGets() {
return getNLocalTopGets(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most read locally by this instance",
displayName = "Nth Top Local Read Keys")
public Map<String, Long> getNLocalTopGets(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.LOCAL_GET, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most write remotely by this instance",
displayName = "Top Remote Write Keys")
public Map<String, Long> getRemoteTopPuts() {
return getNRemoteTopPuts(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most write remotely by this instance",
displayName = "Nth Top Remote Write Keys")
public Map<String, Long> getNRemoteTopPuts(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.REMOTE_PUT, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most write locally by this instance",
displayName = "Top Local Write Keys")
public Map<String, Long> getLocalTopPuts() {
return getNLocalTopPuts(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most write locally by this instance",
displayName = "Nth Top Local Write Keys")
public Map<String, Long> getNLocalTopPuts(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.LOCAL_PUT, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most locked",
displayName = "Top Locked Keys")
public Map<String, Long> getTopLockedKeys() {
return getNTopLockedKeys(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most locked",
displayName = "Nth Top Locked Keys")
public Map<String, Long> getNTopLockedKeys(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.MOST_LOCKED_KEYS, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys most contended",
displayName = "Top Contended Keys")
public Map<String, Long> getTopContendedKeys() {
return getNTopContendedKeys(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys most contended",
displayName = "Nth Top Contended Keys")
public Map<String, Long> getNTopContendedKeys(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.MOST_CONTENDED_KEYS, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys whose lock acquisition failed by timeout",
displayName = "Top Keys whose Lock Acquisition Failed by Timeout")
public Map<String, Long> getTopLockFailedKeys() {
return getNTopLockFailedKeys(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys whose lock acquisition failed ",
displayName = "Nth Top Keys whose Lock Acquisition Failed by Timeout")
public Map<String, Long> getNTopLockFailedKeys(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.MOST_FAILED_KEYS, n);
}
@ManagedAttribute(description = "Show the top " + DEFAULT_TOP_KEY + " keys whose write skew check was failed",
displayName = "Top Keys whose Write Skew Check was failed")
public Map<String, Long> getTopWriteSkewFailedKeys() {
return getNTopWriteSkewFailedKeys(DEFAULT_TOP_KEY);
}
@ManagedOperation(description = "Show the top n keys whose write skew check was failed",
displayName = "Nth Top Keys whose Write Skew Check was failed")
public Map<String, Long> getNTopWriteSkewFailedKeys(@Parameter(name = "n", description = "the n-th top key to return") int n) {
return streamSummaryContainer.getTopKFromAsKeyString(StreamSummaryContainer.Stat.MOST_WRITE_SKEW_FAILED_KEYS, n);
}
@ManagedOperation(description = "Show the top n keys whose write skew check was failed",
displayName = "Top Keys whose Write Skew Check was failed")
public void setStatisticsEnabled(@Parameter(name = "enabled", description = "true to enable the top-k collection") boolean enabled) {
streamSummaryContainer.setEnabled(enabled);
}
@Override
protected void start() {
super.start();
log.startStreamSummaryInterceptor();
streamSummaryContainer = StreamSummaryContainer.getOrCreateStreamLibContainer(cache);
streamSummaryContainer.setEnabled(true);
distributionManager = cache.getAdvancedCache().getDistributionManager();
ComponentRegistry componentRegistry = cache.getAdvancedCache().getComponentRegistry();
LockManager oldLockManager = componentRegistry.getComponent(LockManager.class);
LockManager newLockManager = new TopKeyLockManager(oldLockManager, streamSummaryContainer);
log.replaceComponent("LockManager", oldLockManager, newLockManager);
componentRegistry.registerComponent(newLockManager, LockManager.class);
componentRegistry.rewire();
}
@Override
protected void stop() {
super.stop();
log.stopStreamSummaryInterceptor();
streamSummaryContainer.setEnabled(false);
}
private boolean isRemote(Object key) {
return distributionManager != null && !distributionManager.getCacheTopology().isWriteOwner(key);
}
}