package org.infinispan.functional;
import java.io.Serializable;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.api.functional.EntryView.ReadWriteEntryView;
import org.infinispan.commons.api.functional.FunctionalMap.ReadWriteMap;
import org.infinispan.functional.impl.FunctionalMapImpl;
import org.infinispan.functional.impl.ReadWriteMapImpl;
import org.infinispan.test.TestingUtil;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* @author Krzysztof Sobolewski <Krzysztof.Sobolewski@atende.pl>
*/
@Test(groups = "functional", testName = "functional.FunctionalDistributionTest")
public class FunctionalDistributionTest extends AbstractFunctionalTest {
public FunctionalDistributionTest() {
numNodes = 4;
// we want some non-owners and some secondary owners:
numDistOwners = 2;
/*
* The potential problem arises in async mode: the non-primary owner
* executes, then forwards to the primary owner, which executes and
* forwards to all non-primary owners, including the originator, which
* executes again. In sync mode this does not cause problems because the
* second invocation on the originator happens before the first is
* committed so it receives the same input data. Not so in async mode.
*/
isSync = false;
}
@BeforeClass
@Override
public void createBeforeClass() throws Throwable {
super.createBeforeClass();
}
public void testDistributionFromPrimaryOwner() throws InterruptedException {
Object key = "testDistributionFromPrimaryOwner";
doTestDistribution(key, cacheManagers.stream()
.map(cm -> cm.<Object, Integer>getCache(DIST).getAdvancedCache())
.filter(cache -> cache.getDistributionManager().getPrimaryLocation(key)
.equals(cache.getRpcManager().getAddress()))
.findAny()
.get());
}
public void testDistributionFromSecondaryOwner() throws InterruptedException {
Object key = "testDistributionFromSecondaryOwner";
doTestDistribution(key, cacheManagers.stream()
.map(cm -> cm.<Object, Integer>getCache(DIST).getAdvancedCache())
// owner...
.filter(cache -> cache.getDistributionManager().getLocality(key).isLocal()
// ...but not primary owner
&& !cache.getDistributionManager().getPrimaryLocation(key)
.equals(cache.getRpcManager().getAddress()))
.findAny()
.get());
}
public void testDistributionFromNonOwner() throws InterruptedException {
Object key = "testDistributionFromNonOwner";
doTestDistribution(key, cacheManagers.stream()
.map(cm -> cm.<Object, Integer>getCache(DIST).getAdvancedCache())
.filter(cache -> !cache.getDistributionManager().getLocality(key).isLocal())
.findAny()
.get());
}
private void doTestDistribution(Object key, AdvancedCache<Object, Integer> originator) throws InterruptedException {
ReadWriteMap<Object, Integer> rw = ReadWriteMapImpl.create(FunctionalMapImpl.create(originator));
// with empty cache:
iterate(key, rw, 1);
// again:
iterate(key, rw, 2);
}
private void iterate(Object key, ReadWriteMap<Object, Integer> rw, int expectedValue) throws InterruptedException {
rw.eval(key, (Function<ReadWriteEntryView<Object, Integer>, Void> & Serializable)
entry -> {
// we need a small delay so that the value gets committed before the replication finishes:
TestingUtil.sleepThread(10);
return entry.set(entry.find().orElse(0) + 1);
}).join();
// since we're in async mode, we need to wait before the replication finishes
Thread.sleep(100); // TODO: find a better way?
// we want to ensure that each of the owners executes the function only once:
Assert.assertEquals(
(Object) cacheManagers.stream()
.map(cm -> cm.<Object, Integer>getCache(DIST).getAdvancedCache())
.filter(cache -> cache.getDistributionManager().getLocality(key).isLocal())
.map(cache -> cache.getDataContainer().get(key).getValue())
.collect(Collectors.toList()),
Collections.nCopies(numDistOwners, expectedValue));
}
}