package org.infinispan.util;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.infinispan.Cache;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.topology.ClusterTopologyManager;
/**
* ConsistentHashFactory implementation that allows the user to control who the owners are.
*
* @author Dan Berindei
* @since 7.0
*/
public class ControlledConsistentHashFactory extends BaseControlledConsistentHashFactory {
private volatile List<int[]> ownerIndexes;
private volatile List<Address> membersToUse;
/**
* Create a consistent hash factory with a single segment.
*/
public ControlledConsistentHashFactory(int primaryOwnerIndex, int... backupOwnerIndexes) {
super(1);
setOwnerIndexes(primaryOwnerIndex, backupOwnerIndexes);
}
/**
* Create a consistent hash factory with multiple segments.
*/
public ControlledConsistentHashFactory(int[] firstSegmentOwners, int[]... otherSegmentOwners) {
super(1 + (otherSegmentOwners != null ? otherSegmentOwners.length : 0));
setOwnerIndexes(firstSegmentOwners, otherSegmentOwners);
}
public void setOwnerIndexes(int primaryOwnerIndex, int... backupOwnerIndexes) {
int[] firstSegmentOwners = concatOwners(primaryOwnerIndex, backupOwnerIndexes);
setOwnerIndexes(firstSegmentOwners);
}
private int[] concatOwners(int primaryOwnerIndex, int[] backupOwnerIndexes) {
int[] firstSegmentOwners;
if (backupOwnerIndexes == null || backupOwnerIndexes.length == 0) {
firstSegmentOwners = new int[]{primaryOwnerIndex};
} else {
firstSegmentOwners = new int[backupOwnerIndexes.length + 1];
firstSegmentOwners[0] = primaryOwnerIndex;
for (int i = 0; i < backupOwnerIndexes.length; i++) {
firstSegmentOwners[i + 1] = backupOwnerIndexes[i];
}
}
return firstSegmentOwners;
}
public void setOwnerIndexes(int[] segment1Owners, int[]... otherSegmentOwners) {
ArrayList<int[]> newOwnerIndexes = new ArrayList<int[]>(numSegments);
newOwnerIndexes.add(segment1Owners);
if (otherSegmentOwners != null) {
newOwnerIndexes.addAll(Arrays.asList(otherSegmentOwners));
}
assertEquals(numSegments, newOwnerIndexes.size());
this.ownerIndexes = newOwnerIndexes;
}
public void setOwnerIndexesForSegment(int segmentIndex, int primaryOwnerIndex, int... backupOwnerIndexes) {
ArrayList<int[]> newOwnerIndexes = new ArrayList<int[]>(ownerIndexes);
newOwnerIndexes.set(segmentIndex, concatOwners(primaryOwnerIndex, backupOwnerIndexes));
this.ownerIndexes = newOwnerIndexes;
}
public void triggerRebalance(Cache<?, ?> cache) throws Exception {
EmbeddedCacheManager cacheManager = cache.getCacheManager();
ClusterTopologyManager clusterTopologyManager = cacheManager
.getGlobalComponentRegistry().getComponent(ClusterTopologyManager.class);
assertTrue("triggerRebalance must be called on the coordinator node",
cacheManager.getTransport().isCoordinator());
clusterTopologyManager.forceRebalance(cache.getName());
}
@Override
protected List<Address> createOwnersCollection(List<Address> members, int numberOfOwners, int segmentIndex) {
int[] segmentOwnerIndexes = ownerIndexes.get(segmentIndex);
List<Address> owners = new ArrayList<>(segmentOwnerIndexes.length);
for (int index : segmentOwnerIndexes) {
if (membersToUse != null) {
Address owner = membersToUse.get(index);
if (members.contains(owner)) {
owners.add(owner);
}
} else if (index < members.size()) {
owners.add(members.get(index));
}
}
// A CH segment must always have at least one owner
if (owners.isEmpty()) {
owners.add(members.get(0));
}
return owners;
}
/**
* @param membersToUse Owner indexes will be in this list, instead of the current list of members
*/
public void setMembersToUse(List<Address> membersToUse) {
this.membersToUse = membersToUse;
}
}