package org.infinispan.distribution.impl;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.distribution.DataLocality;
import org.infinispan.distribution.DistributionInfo;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.distribution.LocalizedCacheTopology;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.distribution.ch.KeyPartitioner;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.jmx.annotations.MBean;
import org.infinispan.jmx.annotations.ManagedOperation;
import org.infinispan.jmx.annotations.Parameter;
import org.infinispan.remoting.transport.Address;
import org.infinispan.remoting.transport.Transport;
import org.infinispan.topology.CacheTopology;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* The default distribution manager implementation
*
* @author Manik Surtani
* @author Vladimir Blagojevic
* @author Mircea.Markus@jboss.com
* @author Bela Ban
* @author Dan Berindei <dan@infinispan.org>
* @author anistor@redhat.com
* @since 4.0
*/
@MBean(objectName = "DistributionManager", description = "Component that handles distribution of content across a cluster")
public class DistributionManagerImpl implements DistributionManager {
private static final Log log = LogFactory.getLog(DistributionManagerImpl.class);
private static final boolean trace = log.isTraceEnabled();
// Injected components
private Transport transport;
private KeyPartitioner keyPartitioner;
private CacheMode cacheMode;
private volatile LocalizedCacheTopology extendedTopology;
/**
* Default constructor
*/
public DistributionManagerImpl() {
}
@Inject
public void init(Transport transport, Configuration configuration, KeyPartitioner keyPartitioner) {
this.transport = transport;
this.keyPartitioner = keyPartitioner;
this.cacheMode = configuration.clustering().cacheMode();
}
// Start before RpcManagerImpl
@Start(priority = 8)
@SuppressWarnings("unused")
private void start() throws Exception {
if (trace) log.tracef("starting distribution manager on %s", getAddress());
// We need an extended topology for preload, before the start of StateTransferManagerImpl
Address localAddress = transport.getAddress();
extendedTopology = LocalizedCacheTopology.makeSingletonTopology(cacheMode, localAddress);
}
private Address getAddress() {
return transport.getAddress();
}
@Override
public DataLocality getLocality(Object key) {
LocalizedCacheTopology info = this.extendedTopology;
if (info == null) {
return DataLocality.NOT_LOCAL;
}
DistributionInfo segmentInfo = info.getDistribution(key);
if (segmentInfo.isReadOwner()) {
return DataLocality.LOCAL;
} else if (segmentInfo.isWriteOwner()) {
return DataLocality.LOCAL_UNCERTAIN;
} else {
return DataLocality.NOT_LOCAL;
}
}
@Override
public List<Address> locate(Object key) {
return extendedTopology.getDistribution(key).writeOwners();
}
@Override
public Address getPrimaryLocation(Object key) {
return extendedTopology.getDistribution(key).primary();
}
@Override
public Set<Address> locateAll(Collection<Object> keys) {
Collection<Address> owners = extendedTopology.getWriteOwners(keys);
return new HashSet<>(owners);
}
@Override
public ConsistentHash getReadConsistentHash() {
return extendedTopology.getReadConsistentHash();
}
@Override
public ConsistentHash getWriteConsistentHash() {
return extendedTopology.getWriteConsistentHash();
}
@Override
@ManagedOperation(
description = "Determines whether a given key is affected by an ongoing rehash, if any.",
displayName = "Could key be affected by rehash?"
)
public boolean isAffectedByRehash(@Parameter(name = "key", description = "Key to check") Object key) {
if (!isRehashInProgress())
return false;
int segment = keyPartitioner.getSegment(key);
DistributionInfo distributionInfo = this.extendedTopology.getDistribution(segment);
return distributionInfo.isWriteOwner() && !distributionInfo.isReadOwner();
}
/**
* Tests whether a rehash is in progress
*
* @return true if a rehash is in progress, false otherwise
*/
@Override
public boolean isRehashInProgress() {
return extendedTopology.getPendingCH() != null;
}
@Override
public boolean isJoinComplete() {
return extendedTopology != null;
}
@ManagedOperation(
description = "Tells you whether a given key would be written to this instance of the cache according to the consistent hashing algorithm. " +
"Only works with String keys.",
displayName = "Is key local?"
)
public boolean isLocatedLocally(@Parameter(name = "key", description = "Key to query") String key) {
return getCacheTopology().isWriteOwner(key);
}
@ManagedOperation(
description = "Shows the addresses of the nodes where a write operation would store the entry associated with the specified key. Only " +
"works with String keys.",
displayName = "Locate key"
)
public List<String> locateKey(@Parameter(name = "key", description = "Key to locate") String key) {
List<Address> addresses = getCacheTopology().getDistribution(key).writeOwners();
return addresses.stream()
.map(Address::toString)
.collect(Collectors.toList());
}
@Override
public LocalizedCacheTopology getCacheTopology() {
return this.extendedTopology;
}
@Override
public void setCacheTopology(CacheTopology cacheTopology) {
this.extendedTopology = new LocalizedCacheTopology(cacheMode, cacheTopology, keyPartitioner, transport.getAddress());
}
}