package org.infinispan.xsite.statetransfer.failures;
import static org.infinispan.test.TestingUtil.WrapFactory;
import static org.infinispan.test.TestingUtil.wrapGlobalComponent;
import static org.infinispan.util.BlockingLocalTopologyManager.replaceTopologyManager;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.manager.CacheContainer;
import org.infinispan.remoting.transport.Address;
import org.infinispan.test.fwk.CheckPoint;
import org.infinispan.util.BlockingLocalTopologyManager;
import org.infinispan.xsite.BackupReceiver;
import org.infinispan.xsite.BackupReceiverDelegator;
import org.infinispan.xsite.BackupReceiverRepository;
import org.infinispan.xsite.BackupReceiverRepositoryDelegator;
import org.infinispan.xsite.statetransfer.XSiteState;
import org.infinispan.xsite.statetransfer.XSiteStatePushCommand;
import org.testng.annotations.Test;
/**
* Cross-Site replication state transfer tests. It tests topology changes in consumer site.
*
* @author Pedro Ruivo
* @since 7.0
*/
@Test(groups = "xsite", testName = "xsite.statetransfer.failures.SiteConsumerTopologyChangeTest")
public class SiteConsumerTopologyChangeTest extends AbstractTopologyChangeTest {
public SiteConsumerTopologyChangeTest() {
super();
}
@Test(enabled = false, description = "Will be fixed by ISPN-6228")
public void testJoinDuringXSiteST() throws InterruptedException, ExecutionException, TimeoutException {
doTopologyChangeDuringXSiteST(TopologyEvent.JOIN);
}
public void testLeaveDuringXSiteST() throws InterruptedException, ExecutionException, TimeoutException {
doTopologyChangeDuringXSiteST(TopologyEvent.LEAVE);
}
public void testSiteMasterLeaveDuringXSiteST() throws InterruptedException, ExecutionException, TimeoutException {
doTopologyChangeDuringXSiteST(TopologyEvent.SITE_MASTER_LEAVE);
}
public void testXSiteDuringJoin() throws InterruptedException, ExecutionException, TimeoutException {
doXSiteStateTransferDuringTopologyChange(TopologyEvent.JOIN);
}
public void testXSiteSTDuringLeave() throws InterruptedException, ExecutionException, TimeoutException {
doXSiteStateTransferDuringTopologyChange(TopologyEvent.LEAVE);
}
/**
* Site consumer receives some chunks and then, the topology changes.
*/
private void doTopologyChangeDuringXSiteST(TopologyEvent event) throws TimeoutException, InterruptedException, ExecutionException {
log.debugf("Start topology change during x-site state transfer with %s", event);
initBeforeTest();
final TestCaches<Object, Object> testCaches = createTestCache(event, NYC);
printTestCaches(testCaches);
final CheckPoint checkPoint = new CheckPoint();
final AtomicBoolean discard = new AtomicBoolean(true);
wrapGlobalComponent(cache(NYC, 0).getCacheManager(),
BackupReceiverRepository.class,
new WrapFactory<BackupReceiverRepository, BackupReceiverRepository, CacheContainer>() {
@Override
public BackupReceiverRepository wrap(final CacheContainer wrapOn, final BackupReceiverRepository current) {
return new BackupReceiverRepositoryDelegator(current) {
private final Set<Address> addressSet = new HashSet<>();
@Override
public BackupReceiver getBackupReceiver(String originSiteName, String cacheName) {
return new BackupReceiverDelegator(super.getBackupReceiver(originSiteName, cacheName)) {
@Override
public void handleStateTransferState(XSiteStatePushCommand cmd) throws Exception {
log.debugf("Applying state: %s", cmd);
if (!discard.get()) {
delegate.handleStateTransferState(cmd);
return;
}
DistributionManager manager = delegate.getCache().getAdvancedCache().getDistributionManager();
synchronized (addressSet) {
//discard the state message when all member has received at least one chunk!
if (addressSet.size() == 3) {
checkPoint.trigger("before-block");
checkPoint.awaitStrict("blocked", 30, TimeUnit.SECONDS);
delegate.handleStateTransferState(cmd);
return;
}
for (XSiteState state : cmd.getChunk()) {
addressSet.add(manager.getPrimaryLocation(state.key()));
}
}
delegate.handleStateTransferState(cmd);
}
};
}
};
}
}, true);
log.debug("Start x-site state transfer");
startStateTransfer(testCaches.coordinator, NYC);
assertOnline(LON, NYC);
checkPoint.awaitStrict("before-block", 30, TimeUnit.SECONDS);
Future<?> topologyChangeFuture = triggerTopologyChange(NYC, testCaches.removeIndex);
discard.set(false);
checkPoint.triggerForever("blocked");
topologyChangeFuture.get();
awaitXSiteStateSent(LON);
awaitLocalStateTransfer(NYC);
awaitXSiteStateReceived(NYC);
assertData();
}
private void doXSiteStateTransferDuringTopologyChange(TopologyEvent event) throws TimeoutException, InterruptedException, ExecutionException {
/*
note: this is not tested with SITE_MASTER_LEAVE because it can start the state transfer in NYC.
(startStateTransfer() throws an exception)
*/
log.debugf("Start topology change during x-site state transfer with %s", event);
initBeforeTest();
final TestCaches<Object, Object> testCaches = createTestCache(event, NYC);
printTestCaches(testCaches);
final BlockingLocalTopologyManager topologyManager = replaceTopologyManager(testCaches.controllerCache.getCacheManager());
final CheckPoint checkPoint = new CheckPoint();
wrapGlobalComponent(cache(NYC, 0).getCacheManager(),
BackupReceiverRepository.class,
new WrapFactory<BackupReceiverRepository, BackupReceiverRepository, CacheContainer>() {
@Override
public BackupReceiverRepository wrap(final CacheContainer wrapOn, final BackupReceiverRepository current) {
return new BackupReceiverRepositoryDelegator(current) {
@Override
public BackupReceiver getBackupReceiver(String originSiteName, String cacheName) {
return new BackupReceiverDelegator(super.getBackupReceiver(originSiteName, cacheName)) {
@Override
public void handleStateTransferState(XSiteStatePushCommand cmd) throws Exception {
checkPoint.trigger("before-chunk");
delegate.handleStateTransferState(cmd);
}
};
}
};
}
}, true);
topologyManager.startBlocking(BlockingLocalTopologyManager.LatchType.CONSISTENT_HASH_UPDATE);
final Future<Void> topologyEventFuture = triggerTopologyChange(NYC, testCaches.removeIndex);
topologyManager.waitToBlock(BlockingLocalTopologyManager.LatchType.CONSISTENT_HASH_UPDATE);
log.debug("Start x-site state transfer");
startStateTransfer(testCaches.coordinator, NYC);
assertOnline(LON, NYC);
//in the current implementation, the x-site state transfer is not triggered while the rebalance is in progress.
checkPoint.awaitStrict("before-chunk", 30, TimeUnit.SECONDS);
topologyManager.stopBlockingAll();
topologyEventFuture.get();
awaitXSiteStateSent(LON);
awaitLocalStateTransfer(NYC);
awaitXSiteStateReceived(NYC);
assertData();
}
}