package org.infinispan.query.affinity;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static java.util.stream.IntStream.range;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.infinispan.commons.hash.MurmurHash3;
import org.infinispan.distribution.TestAddress;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.distribution.ch.impl.DefaultConsistentHash;
import org.infinispan.remoting.transport.Address;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "query.ShardDistributionTest")
public class ShardDistributionTest {
private static final int MAX_SEGMENTS = 256;
private static final int MAX_NODES = 10;
@Test
public void testAllocation() throws Exception {
for (int nodes = 1; nodes < MAX_NODES; nodes++) {
for (int segments = 1; segments < MAX_SEGMENTS; segments++) {
for (int shards = 1; shards <= segments; shards++) {
assertAllocation(nodes, segments, shards);
}
}
}
}
@Test
public void testDistributionWithNodesOwningNoSegments() throws Exception {
TestAddress segmentsOwned = new TestAddress(1);
TestAddress noSegmentOwned = new TestAddress(2);
List<Address> members = Arrays.asList(segmentsOwned, noSegmentOwned);
int segments = 256;
int shards = 8;
List<Address>[] owners = new List[segments];
range(0, segments).forEach(s -> owners[s] = singletonList(segmentsOwned));
ConsistentHash ch = new DefaultConsistentHash(MurmurHash3.getInstance(), 2, segments, members, null, owners);
ShardDistribution shardDistribution = new FixedShardsDistribution(ch, shards);
assertEquals(8, shardDistribution.getShardsIdentifiers().size());
assertEquals(8, shardDistribution.getShards(segmentsOwned).size());
assertTrue(shardDistribution.getShards(noSegmentOwned) == null);
}
private static void assertAllocation(int numberOfNodes, int numberOfSegments, int numberOfShards) {
ConsistentHash ch = createConsistentHash(numberOfSegments, numberOfNodes);
FixedShardsDistribution shardDistribution = new FixedShardsDistribution(ch, numberOfShards);
Set<String> allShards = shardDistribution.getShardsIdentifiers();
Set<String> shardsFromSegments = range(0, numberOfSegments).boxed()
.map(shardDistribution::getShardFromSegment)
.collect(Collectors.toSet());
assertEquals(numberOfShards, shardsFromSegments.size());
assertEquals(numberOfShards, allShards.size());
}
private static ConsistentHash createConsistentHash(int numSegments, int numNodes) {
return createConsistentHash(numSegments, numNodes, 1);
}
private static ConsistentHash createConsistentHash(int numSegments, int numNodes, int numOwners) {
if (numSegments == 0) {
return null;
}
List<Address> servers = range(0, numNodes).boxed().map(TestAddress::new).collect(toList());
List<Address>[] owners = new List[numSegments];
range(0, numSegments).boxed().forEach(segment -> {
Collections.rotate(servers, 1);
owners[segment] = new ArrayList<>(servers.subList(0, numOwners));
});
return new DefaultConsistentHash(MurmurHash3.getInstance(), numOwners, numSegments, servers, null, owners);
}
}