org.infinispan.distribution.LocalizedCacheTopology Maven / Gradle / Ivy
package org.infinispan.distribution;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.infinispan.commons.util.ImmutableHopscotchHashSet;
import org.infinispan.commons.util.Immutables;
import org.infinispan.commons.util.IntSet;
import org.infinispan.commons.util.IntSets;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.distribution.ch.KeyPartitioner;
import org.infinispan.distribution.ch.impl.SingleSegmentKeyPartitioner;
import org.infinispan.remoting.transport.Address;
import org.infinispan.topology.CacheTopology;
/**
* Extends {@link CacheTopology} with information about keys owned by the local node.
*
* @author Dan Berindei
* @since 9.0
*/
public class LocalizedCacheTopology extends CacheTopology {
private final Address localAddress;
private boolean connected;
private final Set membersSet;
private final KeyPartitioner keyPartitioner;
private final boolean isDistributed;
private final boolean allLocal;
private final int numSegments;
private final int maxOwners;
private final DistributionInfo[] distributionInfos;
private final boolean isScattered;
private final IntSet localReadSegments;
/**
* @param cacheMode Ignored, the result topology is always LOCAL
* @param localAddress Address of the local node
*/
public static LocalizedCacheTopology makeSingletonTopology(CacheMode cacheMode, Address localAddress) {
List members = Collections.singletonList(localAddress);
CacheTopology cacheTopology = new CacheTopology(-1, -1, null, null, Phase.NO_REBALANCE, members, null);
return new LocalizedCacheTopology(CacheMode.LOCAL, cacheTopology, SingleSegmentKeyPartitioner.getInstance(),
localAddress, false);
}
/**
* Creates a new local topology that has a single address but multiple segments. This is useful when the data
* storage is segmented in some way (ie. segmented store)
* @param keyPartitioner partitioner to decide which segment a given key maps to
* @param numSegments how many segments there are
* @param localAddress the address of this node
* @return segmented topology
*/
public static LocalizedCacheTopology makeSegmentedSingletonTopology(KeyPartitioner keyPartitioner, int numSegments,
Address localAddress) {
return new LocalizedCacheTopology(keyPartitioner, numSegments, localAddress);
}
public LocalizedCacheTopology(CacheMode cacheMode, CacheTopology cacheTopology, KeyPartitioner keyPartitioner,
Address localAddress, boolean connected) {
super(cacheTopology.getTopologyId(), cacheTopology.getRebalanceId(), cacheTopology.getCurrentCH(),
cacheTopology.getPendingCH(), cacheTopology.getUnionCH(), cacheTopology.getPhase(), cacheTopology.getActualMembers(),
cacheTopology.getMembersPersistentUUIDs());
ConsistentHash readCH = getReadConsistentHash();
ConsistentHash writeCH = getWriteConsistentHash();
this.localAddress = localAddress;
this.connected = connected;
this.membersSet = new ImmutableHopscotchHashSet<>(cacheTopology.getMembers());
this.keyPartitioner = keyPartitioner;
this.isDistributed = cacheMode.isDistributed();
isScattered = cacheMode.isScattered();
boolean isReplicated = cacheMode.isReplicated();
if (isDistributed || isScattered) {
this.numSegments = readCH.getNumSegments();
this.distributionInfos = new DistributionInfo[numSegments];
int maxOwners = 1;
IntSet localReadSegments = IntSets.mutableEmptySet(numSegments);
for (int segmentId = 0; segmentId < numSegments; segmentId++) {
Address primary = readCH.locatePrimaryOwnerForSegment(segmentId);
List readOwners = readCH.locateOwnersForSegment(segmentId);
List writeOwners = writeCH.locateOwnersForSegment(segmentId);
Collection writeBackups = isScattered ? Collections.emptyList() : writeOwners.subList(1, writeOwners.size());
this.distributionInfos[segmentId] =
new DistributionInfo(segmentId, primary, readOwners, writeOwners, writeBackups, localAddress);
maxOwners = Math.max(maxOwners, writeOwners.size());
if (readOwners.contains(localAddress)) {
localReadSegments.set(segmentId);
}
}
this.maxOwners = maxOwners;
this.allLocal = false;
this.localReadSegments = IntSets.immutableSet(localReadSegments);
} else if (isReplicated) {
this.numSegments = readCH.getNumSegments();
// Writes must be broadcast to the entire cluster
Map> readOwnersMap = new HashMap<>();
Map> writeOwnersMap = new HashMap<>();
this.distributionInfos = new DistributionInfo[numSegments];
for (int segmentId = 0; segmentId < numSegments; segmentId++) {
int segmentCopy = segmentId;
Address primary = readCH.locatePrimaryOwnerForSegment(segmentId);
List readOwners = readOwnersMap.computeIfAbsent(primary, p ->
Immutables.immutableListCopy(readCH.locateOwnersForSegment(segmentCopy)));
List writeOwners = writeOwnersMap.computeIfAbsent(primary, p ->
Immutables.immutableListCopy(writeCH.locateOwnersForSegment(segmentCopy)));
List writeBackups = writeOwners.subList(1, writeOwners.size());
this.distributionInfos[segmentId] =
new DistributionInfo(segmentId, primary, readOwners, writeOwners, writeBackups, localAddress);
}
this.maxOwners = cacheTopology.getMembers().size();
this.allLocal = readOwnersMap.containsKey(localAddress);
this.localReadSegments = IntSets.immutableRangeSet(allLocal ? numSegments : 0);
} else { // Invalidation/Local
assert cacheMode.isInvalidation() || cacheMode == CacheMode.LOCAL;
this.numSegments = 1;
// Reads and writes are local, only the invalidation is replicated
List owners = Collections.singletonList(localAddress);
List writeBackups = Collections.emptyList();
this.distributionInfos = new DistributionInfo[]{
new DistributionInfo(0, localAddress, owners, owners, writeBackups, localAddress)
};
this.maxOwners = 1;
this.allLocal = true;
this.localReadSegments = IntSets.immutableRangeSet(numSegments);
}
}
private LocalizedCacheTopology(KeyPartitioner keyPartitioner, int numSegments, Address localAddress) {
super(-1, -1, null, null, null, Collections.singletonList(localAddress), null);
this.localAddress = localAddress;
this.numSegments = numSegments;
this.keyPartitioner = keyPartitioner;
this.membersSet = Collections.unmodifiableSet(Collections.singleton(localAddress));
this.isDistributed = false;
this.isScattered = false;
// Reads and writes are local, only the invalidation is replicated
List owners = Collections.singletonList(localAddress);
this.distributionInfos = new DistributionInfo[numSegments];
for (int i = 0; i < distributionInfos.length; ++i) {
distributionInfos[i] = new DistributionInfo(i, localAddress, owners, owners, Collections.emptyList(), localAddress);
}
this.maxOwners = 1;
this.allLocal = true;
this.localReadSegments = IntSets.immutableRangeSet(numSegments);
}
/**
* @return {@code true} iff key {@code key} can be read without going remote.
*/
public boolean isReadOwner(Object key) {
if (allLocal)
return true;
int segmentId = keyPartitioner.getSegment(key);
return distributionInfos[segmentId].isReadOwner();
}
public boolean isSegmentReadOwner(int segment) {
return allLocal || distributionInfos[segment].isReadOwner();
}
/**
* @return {@code true} iff writing a value for key {@code key} will update it on the local node.
*/
public boolean isWriteOwner(Object key) {
if (allLocal)
return true;
int segmentId = keyPartitioner.getSegment(key);
return distributionInfos[segmentId].isWriteOwner();
}
public boolean isSegmentWriteOwner(int segment) {
return allLocal || distributionInfos[segment].isWriteOwner();
}
/**
* @return The consistent hash segment of key {@code key}
*/
public int getSegment(Object key) {
return keyPartitioner.getSegment(key);
}
/**
* @return Information about the ownership of segment {@code segment}, including the primary owner.
* @deprecated since 9.3 please use {@link #getSegmentDistribution(int)} instead.
*/
@Deprecated
public DistributionInfo getDistributionForSegment(int segmentId) {
return getSegmentDistribution(segmentId);
}
public DistributionInfo getSegmentDistribution(int segmentId) {
return distributionInfos[segmentId];
}
/**
* @return Information about the ownership of key {@code key}, including the primary owner.
*/
public DistributionInfo getDistribution(Object key) {
int segmentId = keyPartitioner.getSegment(key);
return distributionInfos[segmentId];
}
/**
* @return An unordered collection with the write owners of {@code key}.
*/
public Collection getWriteOwners(Object key) {
int segmentId = isDistributed || isScattered ? keyPartitioner.getSegment(key) : 0;
return distributionInfos[segmentId].writeOwners();
}
/**
* @return An unordered collection with the write owners of {@code keys}.
*/
public Collection getWriteOwners(Collection> keys) {
if (keys.isEmpty()) {
return Collections.emptySet();
}
if (isDistributed || isScattered) {
if (keys.size() == 1) {
Object singleKey = keys.iterator().next();
return getDistribution(singleKey).writeOwners();
} else {
IntSet segments = IntSets.mutableEmptySet(numSegments);
// Expecting some overlap between keys
Set owners = new HashSet<>(2 * maxOwners);
for (Object key : keys) {
int segment = keyPartitioner.getSegment(key);
if (segments.add(segment)) {
owners.addAll(getSegmentDistribution(segment).writeOwners());
}
}
return owners;
}
} else {
return getSegmentDistribution(0).writeOwners();
}
}
/**
* @return The segments owned by the local node for reading.
*/
public IntSet getLocalReadSegments() {
return localReadSegments;
}
/**
* @return The address of the local node.
*/
public Address getLocalAddress() {
return localAddress;
}
public Set getMembersSet() {
return membersSet;
}
/**
* @return {@code true} if the local node received this topology from the coordinator,
* {@code false} otherwise (e.g. during preload).
*/
public boolean isConnected() {
return connected;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy