package org.infinispan.stats; import static java.lang.String.format; import static org.infinispan.distribution.DistributionTestHelper.addressOf; import org.infinispan.Cache; import org.infinispan.commands.tx.PrepareCommand; import org.infinispan.context.impl.TxInvocationContext; import org.infinispan.distribution.DistributionManager; import org.infinispan.interceptors.AsyncInterceptorChain; import org.infinispan.interceptors.DDAsyncInterceptor; import org.infinispan.interceptors.impl.TxInterceptor; import org.infinispan.stats.topK.CacheUsageInterceptor; import org.infinispan.test.MultipleCacheManagersTest; /** * @author Pedro Ruivo * @since 9.0 */ public abstract class AbstractTopKeyTest extends MultipleCacheManagersTest { private static boolean isOwner(Cache<?, ?> cache, Object key) { DistributionManager dm = cache.getAdvancedCache().getDistributionManager(); return dm == null || dm.locate(key).contains(addressOf(cache)); } protected CacheUsageInterceptor getTopKey(Cache<?, ?> cache) { AsyncInterceptorChain interceptorChain = cache.getAdvancedCache().getAsyncInterceptorChain(); return interceptorChain.findInterceptorExtending(CacheUsageInterceptor.class); } protected void assertTopKeyAccesses(Cache<?, ?> cache, String key, long expected, boolean readAccesses) { final CacheUsageInterceptor topK = getTopKey(cache); final boolean isLocal = isOwner(cache, key); eventuallyEquals(format("Wrong number of accesses for key '%s' and cache '%s'.", key, addressOf(cache)), expected, () -> { if (readAccesses) { return (isLocal ? topK.getLocalTopGets() : topK.getRemoteTopGets()).getOrDefault(key, 0L); } else { return (isLocal ? topK.getLocalTopPuts() : topK.getRemoteTopPuts()).getOrDefault(key, 0L); } }); } protected void assertWriteSkew(Cache<?, ?> cache, String key, long expected) { final CacheUsageInterceptor topK = getTopKey(cache); eventuallyEquals(format("Wrong number of write skew for key '%s' and cache '%s'.", key, addressOf(cache)), expected, () -> topK.getTopWriteSkewFailedKeys().getOrDefault(key, 0L)); } private void assertTopKeyLocked(Cache<?, ?> cache, String key, long expected) { final CacheUsageInterceptor topK = getTopKey(cache); eventuallyEquals(format("Wrong number of locked key for key '%s' and cache '%s'.", key, addressOf(cache)), expected, () -> topK.getTopLockedKeys().getOrDefault(key, 0L)); } private void assertTopKeyLockContented(Cache<?, ?> cache, String key, long expected) { final CacheUsageInterceptor topK = getTopKey(cache); eventuallyEquals(format("Wrong number of contented key for key '%s' and cache '%s'.", key, addressOf(cache)), expected, () -> topK.getTopContendedKeys().getOrDefault(key, 0L)); } private void assertTopKeyLockFailed(Cache<?, ?> cache, String key, long expected) { final CacheUsageInterceptor topK = getTopKey(cache); eventuallyEquals(format("Wrong number of failed locked key for key '%s' and cache '%s'.", key, addressOf(cache)), expected, () -> topK.getTopLockFailedKeys().getOrDefault(key, 0L)); } protected void assertLockInformation(Cache<?, ?> cache, String key, long locked, long contented, long failed) { assertTopKeyLocked(cache, key, locked); assertTopKeyLockContented(cache, key, contented); assertTopKeyLockFailed(cache, key, failed); } protected PrepareCommandBlocker addPrepareBlockerIfAbsent(Cache<?, ?> cache) { AsyncInterceptorChain chain = cache.getAdvancedCache().getAsyncInterceptorChain(); PrepareCommandBlocker blocker = chain.findInterceptorWithClass(PrepareCommandBlocker.class); if (blocker != null) return blocker; blocker = new PrepareCommandBlocker(); chain.addInterceptorBefore(blocker, TxInterceptor.class); return blocker; } protected class PrepareCommandBlocker extends DDAsyncInterceptor { private boolean unblock = false; private boolean prepareBlocked = false; @Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { return invokeNextThenAccept(ctx, command, (rCtx, rCommand, rv) -> { synchronized (this) { prepareBlocked = true; notifyAll(); while (!unblock) { wait(); } } }); } public synchronized void reset() { unblock = false; prepareBlocked = false; } public synchronized void unblock() { unblock = true; notifyAll(); } public synchronized void awaitUntilPrepareBlocked() throws InterruptedException { while (!prepareBlocked) { wait(); } } } }