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; } }