package org.infinispan.util;
import org.infinispan.manager.CacheContainer;
import org.infinispan.test.TestingUtil;
import org.infinispan.topology.CacheTopology;
import org.infinispan.topology.LocalTopologyManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Replaces the LocalTopologyManager and allows it to block the phases of the state transfer:
* <ul>
* <li>Rebalance Start</li>
* <li>Confirm Rebalance</li>
* <li>Consistent Hash Update</li>
* </ul>
*
* @author Pedro Ruivo
* @since 6.0
*/
public class BlockingLocalTopologyManager extends AbstractControlledLocalTopologyManager {
private final Log log = LogFactory.getLog(BlockingLocalTopologyManager.class);
private final NotifierLatch blockConfirmRebalance;
private final NotifierLatch blockConsistentHashUpdate;
private final NotifierLatch blockRebalanceStart;
public BlockingLocalTopologyManager(LocalTopologyManager delegate) {
super(delegate);
blockRebalanceStart = new NotifierLatch();
blockConsistentHashUpdate = new NotifierLatch();
blockConfirmRebalance = new NotifierLatch();
}
public static BlockingLocalTopologyManager replaceTopologyManager(CacheContainer cacheContainer) {
LocalTopologyManager manager = TestingUtil.extractGlobalComponent(cacheContainer, LocalTopologyManager.class);
BlockingLocalTopologyManager controlledLocalTopologyManager = new BlockingLocalTopologyManager(manager);
TestingUtil.replaceComponent(cacheContainer, LocalTopologyManager.class, controlledLocalTopologyManager, true);
return controlledLocalTopologyManager;
}
public void startBlocking(LatchType type) {
getLatch(type).startBlocking();
}
public void stopBlocking(LatchType type) {
getLatch(type).stopBlocking();
}
public void waitToBlock(LatchType type) throws InterruptedException {
getLatch(type).waitToBlock();
}
public void unblockOnce(LatchType type) {
getLatch(type).unblockOnce();
}
public void waitToBlockAndUnblockOnce(LatchType type) throws InterruptedException {
getLatch(type).waitToBlockAndUnblockOnce();
}
public void stopBlockingAll() {
for (LatchType type : LatchType.values()) {
getLatch(type).stopBlocking();
}
}
@Override
protected final void beforeHandleTopologyUpdate(String cacheName, CacheTopology cacheTopology, int viewId) {
log.debugf("Before consistent hash update %s", cacheTopology);
getLatch(LatchType.CONSISTENT_HASH_UPDATE).blockIfNeeded();
log.debugf("Continue consistent hash update %s", cacheTopology);
}
@Override
protected final void beforeHandleRebalance(String cacheName, CacheTopology cacheTopology, int viewId) {
log.debugf("Before rebalance %s", cacheTopology);
getLatch(LatchType.REBALANCE).blockIfNeeded();
log.debugf("Continue rebalance %s", cacheTopology);
}
@Override
protected final void beforeConfirmRebalancePhase(String cacheName, int topologyId, Throwable throwable) {
log.debugf("Before confirm topology %d", topologyId);
getLatch(LatchType.CONFIRM_REBALANCE_PHASE).blockIfNeeded();
log.debugf("Continue confirm topology %d", topologyId);
}
private NotifierLatch getLatch(LatchType type) {
switch (type) {
case CONSISTENT_HASH_UPDATE:
return blockConsistentHashUpdate;
case CONFIRM_REBALANCE_PHASE:
return blockConfirmRebalance;
case REBALANCE:
return blockRebalanceStart;
}
throw new IllegalStateException("Should never happen!");
}
public static enum LatchType {
CONSISTENT_HASH_UPDATE,
CONFIRM_REBALANCE_PHASE,
REBALANCE
}
}