Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.redisson.cluster.ClusterConnectionManager Maven / Gradle / Ivy
/**
* Copyright 2018 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.cluster;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.redisson.api.NodeType;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisClientConfig;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisConnectionException;
import org.redisson.client.RedisException;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.RedisStrictCommand;
import org.redisson.cluster.ClusterNodeInfo.Flag;
import org.redisson.cluster.ClusterPartition.Type;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReadMode;
import org.redisson.connection.CRC16;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.connection.MasterSlaveConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.SingleEntry;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.resolver.AddressResolver;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.PlatformDependent;
/**
*
* @author Nikita Koksharov
*
*/
public class ClusterConnectionManager extends MasterSlaveConnectionManager {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ConcurrentMap lastPartitions = PlatformDependent.newConcurrentHashMap();
private ScheduledFuture> monitorFuture;
private volatile URI lastClusterNode;
private RedisStrictCommand> clusterNodesCommand;
private String configEndpointHostName;
private boolean isConfigEndpoint;
public ClusterConnectionManager(ClusterServersConfig cfg, Config config, UUID id) {
super(config, id);
if (cfg.getNodeAddresses().isEmpty()) {
throw new IllegalArgumentException("At least one cluster node should be defined!");
}
this.config = create(cfg);
initTimer(this.config);
Throwable lastException = null;
List failedMasters = new ArrayList();
for (URI addr : cfg.getNodeAddresses()) {
RFuture connectionFuture = connectToNode(cfg, addr, null, addr.getHost());
try {
RedisConnection connection = connectionFuture.syncUninterruptibly().getNow();
if (cfg.getNodeAddresses().size() == 1 && NetUtil.createByteArrayFromIpAddressString(addr.getHost()) == null) {
configEndpointHostName = addr.getHost();
isConfigEndpoint = true;
}
clusterNodesCommand = RedisCommands.CLUSTER_NODES;
if ("rediss".equals(addr.getScheme())) {
clusterNodesCommand = RedisCommands.CLUSTER_NODES_SSL;
}
List nodes = connection.sync(clusterNodesCommand);
StringBuilder nodesValue = new StringBuilder();
for (ClusterNodeInfo clusterNodeInfo : nodes) {
nodesValue.append(clusterNodeInfo.getNodeInfo()).append("\n");
}
log.info("Redis cluster nodes configuration got from {}:\n{}", connection.getRedisClient().getAddr(), nodesValue);
lastClusterNode = addr;
Collection partitions = parsePartitions(nodes);
List>>> futures = new ArrayList>>>();
for (ClusterPartition partition : partitions) {
if (partition.isMasterFail()) {
failedMasters.add(partition.getMasterAddress().toString());
continue;
}
RFuture>> masterFuture = addMasterEntry(partition, cfg);
futures.add(masterFuture);
}
for (RFuture>> masterFuture : futures) {
masterFuture.awaitUninterruptibly();
if (!masterFuture.isSuccess()) {
lastException = masterFuture.cause();
continue;
}
for (RFuture future : masterFuture.getNow()) {
future.awaitUninterruptibly();
if (!future.isSuccess()) {
lastException = future.cause();
}
}
}
break;
} catch (Exception e) {
lastException = e;
log.warn(e.getMessage());
}
}
if (lastPartitions.isEmpty()) {
stopThreads();
if (failedMasters.isEmpty()) {
throw new RedisConnectionException("Can't connect to servers!", lastException);
} else {
throw new RedisConnectionException("Can't connect to servers! Failed masters according to cluster status: " + failedMasters, lastException);
}
}
if (lastPartitions.size() != MAX_SLOT) {
stopThreads();
if (failedMasters.isEmpty()) {
throw new RedisConnectionException("Not all slots are covered! Only " + lastPartitions.size() + " slots are avaliable", lastException);
} else {
throw new RedisConnectionException("Not all slots are covered! Only " + lastPartitions.size() + " slots are avaliable. Failed masters according to cluster status: " + failedMasters, lastException);
}
}
scheduleClusterChangeCheck(cfg, null);
}
@Override
protected RedisClientConfig createRedisConfig(NodeType type, URI address, int timeout, int commandTimeout, String sslHostname) {
RedisClientConfig result = super.createRedisConfig(type, address, timeout, commandTimeout, sslHostname);
result.setReadOnly(type == NodeType.SLAVE && config.getReadMode() != ReadMode.MASTER);
return result;
}
private RFuture>> addMasterEntry(final ClusterPartition partition, final ClusterServersConfig cfg) {
if (partition.isMasterFail()) {
RedisException e = new RedisException("Failed to add master: " +
partition.getMasterAddress() + " for slot ranges: " +
partition.getSlotRanges() + ". Reason - server has FAIL flag");
if (partition.getSlotRanges().isEmpty()) {
e = new RedisException("Failed to add master: " +
partition.getMasterAddress() + ". Reason - server has FAIL flag");
}
return RedissonPromise.newFailedFuture(e);
}
final RPromise>> result = new RedissonPromise>>();
RFuture connectionFuture = connectToNode(cfg, partition.getMasterAddress(), null, configEndpointHostName);
connectionFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't connect to master: {} with slot ranges: {}", partition.getMasterAddress(), partition.getSlotRanges());
result.tryFailure(future.cause());
return;
}
final RedisConnection connection = future.getNow();
RFuture> clusterFuture = connection.async(RedisCommands.CLUSTER_INFO);
clusterFuture.addListener(new FutureListener>() {
@Override
public void operationComplete(Future> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't execute CLUSTER_INFO for " + connection.getRedisClient().getAddr(), future.cause());
result.tryFailure(future.cause());
return;
}
Map params = future.getNow();
if ("fail".equals(params.get("cluster_state"))) {
RedisException e = new RedisException("Failed to add master: " +
partition.getMasterAddress() + " for slot ranges: " +
partition.getSlotRanges() + ". Reason - cluster_state:fail");
log.error("cluster_state:fail for " + connection.getRedisClient().getAddr());
result.tryFailure(e);
return;
}
MasterSlaveServersConfig config = create(cfg);
config.setMasterAddress(partition.getMasterAddress());
final MasterSlaveEntry e;
List> futures = new ArrayList>();
if (config.checkSkipSlavesInit()) {
e = new SingleEntry(ClusterConnectionManager.this, config);
} else {
config.setSlaveAddresses(partition.getSlaveAddresses());
e = new MasterSlaveEntry(ClusterConnectionManager.this, config);
List> fs = e.initSlaveBalancer(partition.getFailedSlaveAddresses());
futures.addAll(fs);
if (!partition.getSlaveAddresses().isEmpty()) {
log.info("slaves: {} added for slot ranges: {}", partition.getSlaveAddresses(), partition.getSlotRanges());
if (!partition.getFailedSlaveAddresses().isEmpty()) {
log.warn("slaves: {} is down for slot ranges: {}", partition.getFailedSlaveAddresses(), partition.getSlotRanges());
}
}
}
RFuture f = e.setupMasterEntry(config.getMasterAddress());
final RPromise initFuture = new RedissonPromise();
futures.add(initFuture);
f.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't add master: {} for slot ranges: {}", partition.getMasterAddress(), partition.getSlotRanges());
initFuture.tryFailure(future.cause());
return;
}
for (Integer slot : partition.getSlots()) {
addEntry(slot, e);
lastPartitions.put(slot, partition);
}
log.info("master: {} added for slot ranges: {}", partition.getMasterAddress(), partition.getSlotRanges());
if (!initFuture.trySuccess(null)) {
throw new IllegalStateException();
}
}
});
if (!result.trySuccess(futures)) {
throw new IllegalStateException();
}
}
});
}
});
return result;
}
private void scheduleClusterChangeCheck(final ClusterServersConfig cfg, final Iterator iterator) {
monitorFuture = group.schedule(new Runnable() {
@Override
public void run() {
if (isConfigEndpoint) {
final URI uri = cfg.getNodeAddresses().iterator().next();
final AddressResolver resolver = resolverGroup.getResolver(getGroup().next());
Future> allNodes = resolver.resolveAll(InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()));
allNodes.addListener(new FutureListener>() {
@Override
public void operationComplete(Future> future) throws Exception {
AtomicReference lastException = new AtomicReference(future.cause());
if (!future.isSuccess()) {
checkClusterState(cfg, Collections.emptyList().iterator(), lastException);
return;
}
List nodes = new ArrayList();
for (InetSocketAddress addr : future.getNow()) {
URI node = URIBuilder.create(uri.getScheme() + "://" + addr.getAddress().getHostAddress() + ":" + addr.getPort());
nodes.add(node);
}
Iterator nodesIterator = nodes.iterator();
checkClusterState(cfg, nodesIterator, lastException);
}
});
} else {
AtomicReference lastException = new AtomicReference();
Iterator nodesIterator = iterator;
if (nodesIterator == null) {
List nodes = new ArrayList();
List slaves = new ArrayList();
for (ClusterPartition partition : getLastPartitions()) {
if (!partition.isMasterFail()) {
nodes.add(partition.getMasterAddress());
}
Set partitionSlaves = new HashSet(partition.getSlaveAddresses());
partitionSlaves.removeAll(partition.getFailedSlaveAddresses());
slaves.addAll(partitionSlaves);
}
// master nodes first
nodes.addAll(slaves);
nodesIterator = nodes.iterator();
}
checkClusterState(cfg, nodesIterator, lastException);
}
}
}, cfg.getScanInterval(), TimeUnit.MILLISECONDS);
}
private void checkClusterState(final ClusterServersConfig cfg, final Iterator iterator, final AtomicReference lastException) {
if (!iterator.hasNext()) {
if (lastException.get() != null) {
log.error("Can't update cluster state", lastException.get());
}
scheduleClusterChangeCheck(cfg, null);
return;
}
if (!getShutdownLatch().acquire()) {
return;
}
final URI uri = iterator.next();
RFuture connectionFuture = connectToNode(cfg, uri, null, configEndpointHostName);
connectionFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
lastException.set(future.cause());
getShutdownLatch().release();
checkClusterState(cfg, iterator, lastException);
return;
}
RedisConnection connection = future.getNow();
updateClusterState(cfg, connection, iterator, uri, lastException);
}
});
}
private void updateClusterState(final ClusterServersConfig cfg, final RedisConnection connection,
final Iterator iterator, final URI uri, final AtomicReference lastException) {
RFuture> future = connection.async(clusterNodesCommand);
future.addListener(new FutureListener>() {
@Override
public void operationComplete(Future> future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't execute CLUSTER_NODES with " + connection.getRedisClient().getAddr(), future.cause());
closeNodeConnection(connection);
lastException.set(future.cause());
getShutdownLatch().release();
checkClusterState(cfg, iterator, lastException);
return;
}
lastClusterNode = uri;
List nodes = future.getNow();
final StringBuilder nodesValue = new StringBuilder();
if (log.isDebugEnabled()) {
for (ClusterNodeInfo clusterNodeInfo : nodes) {
nodesValue.append(clusterNodeInfo.getNodeInfo()).append("\n");
}
log.debug("cluster nodes state from {}:\n{}", connection.getRedisClient().getAddr(), nodesValue);
}
final Collection newPartitions = parsePartitions(nodes);
RFuture masterFuture = checkMasterNodesChange(cfg, newPartitions);
checkSlaveNodesChange(newPartitions);
masterFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
checkSlotsMigration(newPartitions);
checkSlotsChange(cfg, newPartitions);
getShutdownLatch().release();
scheduleClusterChangeCheck(cfg, null);
}
});
}
});
}
private void checkSlaveNodesChange(Collection newPartitions) {
Set lastPartitions = getLastPartitions();
for (ClusterPartition newPart : newPartitions) {
for (ClusterPartition currentPart : lastPartitions) {
if (!newPart.getMasterAddress().equals(currentPart.getMasterAddress())) {
continue;
}
MasterSlaveEntry entry = getEntry(currentPart.getSlots().iterator().next());
// should be invoked first in order to remove stale failedSlaveAddresses
Set addedSlaves = addRemoveSlaves(entry, currentPart, newPart);
// Do some slaves have changed state from failed to alive?
upDownSlaves(entry, currentPart, newPart, addedSlaves);
break;
}
}
}
private void upDownSlaves(final MasterSlaveEntry entry, final ClusterPartition currentPart, final ClusterPartition newPart, Set addedSlaves) {
Set aliveSlaves = new HashSet(currentPart.getFailedSlaveAddresses());
aliveSlaves.removeAll(addedSlaves);
aliveSlaves.removeAll(newPart.getFailedSlaveAddresses());
for (URI uri : aliveSlaves) {
currentPart.removeFailedSlaveAddress(uri);
if (entry.hasSlave(uri) && entry.slaveUp(uri, FreezeReason.MANAGER)) {
log.info("slave: {} has up for slot ranges: {}", uri, currentPart.getSlotRanges());
}
}
Set failedSlaves = new HashSet(newPart.getFailedSlaveAddresses());
failedSlaves.removeAll(currentPart.getFailedSlaveAddresses());
for (URI uri : failedSlaves) {
currentPart.addFailedSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges());
}
}
}
private Set addRemoveSlaves(final MasterSlaveEntry entry, final ClusterPartition currentPart, ClusterPartition newPart) {
Set removedSlaves = new HashSet(currentPart.getSlaveAddresses());
removedSlaves.removeAll(newPart.getSlaveAddresses());
for (URI uri : removedSlaves) {
currentPart.removeSlaveAddress(uri);
if (entry.slaveDown(uri, FreezeReason.MANAGER)) {
log.info("slave {} removed for slot ranges: {}", uri, currentPart.getSlotRanges());
}
}
Set addedSlaves = new HashSet(newPart.getSlaveAddresses());
addedSlaves.removeAll(currentPart.getSlaveAddresses());
for (final URI uri : addedSlaves) {
RFuture future = entry.addSlave(uri);
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't add slave: " + uri, future.cause());
return;
}
currentPart.addSlaveAddress(uri);
entry.slaveUp(uri, FreezeReason.MANAGER);
log.info("slave: {} added for slot ranges: {}", uri, currentPart.getSlotRanges());
}
});
}
return addedSlaves;
}
private int slotsAmount(Collection partitions) {
int result = 0;
for (ClusterPartition clusterPartition : partitions) {
result += clusterPartition.getSlots().size();
}
return result;
}
private ClusterPartition find(Collection partitions, Integer slot) {
for (ClusterPartition clusterPartition : partitions) {
for (ClusterSlotRange slotRange : clusterPartition.getSlotRanges()) {
if (slotRange.isOwn(slot)) {
return clusterPartition;
}
}
}
return null;
}
private RFuture checkMasterNodesChange(ClusterServersConfig cfg, Collection newPartitions) {
List newMasters = new ArrayList();
Set lastPartitions = getLastPartitions();
for (final ClusterPartition newPart : newPartitions) {
boolean masterFound = false;
for (final ClusterPartition currentPart : lastPartitions) {
if (!newPart.getMasterAddress().equals(currentPart.getMasterAddress())) {
continue;
}
masterFound = true;
// current master marked as failed
if (!newPart.isMasterFail()) {
continue;
}
for (Integer slot : currentPart.getSlots()) {
ClusterPartition newMasterPart = find(newPartitions, slot);
// does partition has a new master?
if (!newMasterPart.getMasterAddress().equals(currentPart.getMasterAddress())) {
URI newUri = newMasterPart.getMasterAddress();
final URI oldUri = currentPart.getMasterAddress();
RFuture future = changeMaster(slot, newUri);
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
currentPart.setMasterAddress(oldUri);
}
}
});
currentPart.setMasterAddress(newUri);
}
}
break;
}
if (!masterFound && !newPart.getSlotRanges().isEmpty()) {
newMasters.add(newPart);
}
}
if (newMasters.isEmpty()) {
return RedissonPromise.newSucceededFuture(null);
}
final RPromise result = new RedissonPromise();
final AtomicInteger masters = new AtomicInteger(newMasters.size());
final Queue> futures = new ConcurrentLinkedQueue>();
for (ClusterPartition newPart : newMasters) {
RFuture>> future = addMasterEntry(newPart, cfg);
future.addListener(new FutureListener>>() {
@Override
public void operationComplete(Future>> future) throws Exception {
if (future.isSuccess()) {
futures.addAll(future.getNow());
}
if (masters.decrementAndGet() == 0) {
final AtomicInteger nodes = new AtomicInteger(futures.size());
for (RFuture nodeFuture : futures) {
nodeFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (nodes.decrementAndGet() == 0) {
result.trySuccess(null);
}
}
});
}
}
}
});
}
return result;
}
private void checkSlotsChange(ClusterServersConfig cfg, Collection newPartitions) {
int newSlotsAmount = slotsAmount(newPartitions);
if (newSlotsAmount == lastPartitions.size() && lastPartitions.size() == MAX_SLOT) {
return;
}
Set removedSlots = new HashSet();
for (Integer slot : lastPartitions.keySet()) {
boolean found = false;
for (ClusterPartition clusterPartition : newPartitions) {
if (clusterPartition.getSlots().contains(slot)) {
found = true;
break;
}
}
if (!found) {
removedSlots.add(slot);
}
}
lastPartitions.keySet().removeAll(removedSlots);
if (!removedSlots.isEmpty()) {
log.info("{} slots found to remove", removedSlots.size());
}
for (Integer slot : removedSlots) {
MasterSlaveEntry entry = removeEntry(slot);
if (entry.getReferences() == 0) {
entry.shutdownAsync();
log.info("{} master and slaves for it removed", entry.getClient().getAddr());
}
}
Set addedSlots = new HashSet();
for (ClusterPartition clusterPartition : newPartitions) {
for (Integer slot : clusterPartition.getSlots()) {
if (!lastPartitions.containsKey(slot)) {
addedSlots.add(slot);
}
}
}
if (!addedSlots.isEmpty()) {
log.info("{} slots found to add", addedSlots.size());
}
for (final Integer slot : addedSlots) {
ClusterPartition partition = find(newPartitions, slot);
Set oldSlots = new HashSet(partition.getSlots());
oldSlots.removeAll(addedSlots);
if (oldSlots.isEmpty()) {
continue;
}
MasterSlaveEntry entry = getEntry(oldSlots.iterator().next());
if (entry != null) {
addEntry(slot, entry);
lastPartitions.put(slot, partition);
}
}
}
private void checkSlotsMigration(Collection newPartitions) {
for (ClusterPartition currentPartition : getLastPartitions()) {
for (ClusterPartition newPartition : newPartitions) {
if (!currentPartition.getNodeId().equals(newPartition.getNodeId())) {
continue;
}
MasterSlaveEntry entry = getEntry(currentPartition.getSlots().iterator().next());
Set addedSlots = new HashSet(newPartition.getSlots());
addedSlots.removeAll(currentPartition.getSlots());
currentPartition.addSlots(addedSlots);
for (Integer slot : addedSlots) {
addEntry(slot, entry);
lastPartitions.put(slot, currentPartition);
}
if (!addedSlots.isEmpty()) {
log.info("{} slots added to {}", addedSlots.size(), currentPartition.getMasterAddress());
}
Set removedSlots = new HashSet(currentPartition.getSlots());
removedSlots.removeAll(newPartition.getSlots());
for (Integer removeSlot : removedSlots) {
if (lastPartitions.remove(removeSlot, currentPartition)) {
removeEntry(removeSlot);
}
}
currentPartition.removeSlots(removedSlots);
if (!removedSlots.isEmpty()) {
log.info("{} slots removed from {}", removedSlots.size(), currentPartition.getMasterAddress());
}
break;
}
}
}
public String getConfigEndpointHostName() {
return configEndpointHostName;
}
private int indexOf(byte[] array, byte element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
return i;
}
}
return -1;
}
@Override
public int calcSlot(byte[] key) {
if (key == null) {
return 0;
}
int start = indexOf(key, (byte)'{');
if (start != -1) {
int end = indexOf(key, (byte)'}');
key = Arrays.copyOfRange(key, start+1, end);
}
int result = CRC16.crc16(key) % MAX_SLOT;
return result;
}
@Override
public int calcSlot(String key) {
if (key == null) {
return 0;
}
int start = key.indexOf('{');
if (start != -1) {
int end = key.indexOf('}');
key = key.substring(start+1, end);
}
int result = CRC16.crc16(key.getBytes()) % MAX_SLOT;
log.debug("slot {} for {}", result, key);
return result;
}
private Collection parsePartitions(List nodes) {
Map partitions = new HashMap();
for (ClusterNodeInfo clusterNodeInfo : nodes) {
if (clusterNodeInfo.containsFlag(Flag.NOADDR) || clusterNodeInfo.containsFlag(Flag.HANDSHAKE)) {
// skip it
continue;
}
String id = clusterNodeInfo.getNodeId();
ClusterPartition slavePartition = getPartition(partitions, id);
if (clusterNodeInfo.containsFlag(Flag.SLAVE)) {
id = clusterNodeInfo.getSlaveOf();
}
ClusterPartition partition = getPartition(partitions, id);
if (clusterNodeInfo.containsFlag(Flag.SLAVE)) {
slavePartition.setParent(partition);
partition.addSlaveAddress(clusterNodeInfo.getAddress());
if (clusterNodeInfo.containsFlag(Flag.FAIL)) {
partition.addFailedSlaveAddress(clusterNodeInfo.getAddress());
}
} else {
partition.addSlotRanges(clusterNodeInfo.getSlotRanges());
partition.setMasterAddress(clusterNodeInfo.getAddress());
partition.setType(Type.MASTER);
if (clusterNodeInfo.containsFlag(Flag.FAIL)) {
partition.setMasterFail(true);
}
}
}
addCascadeSlaves(partitions);
return partitions.values();
}
private void addCascadeSlaves(Map partitions) {
Iterator iter = partitions.values().iterator();
while (iter.hasNext()) {
ClusterPartition cp = iter.next();
if (cp.getType() != Type.SLAVE) {
continue;
}
if (cp.getParent() != null && cp.getParent().getType() == Type.MASTER) {
ClusterPartition parent = cp.getParent();
for (URI addr : cp.getSlaveAddresses()) {
parent.addSlaveAddress(addr);
}
for (URI addr : cp.getFailedSlaveAddresses()) {
parent.addFailedSlaveAddress(addr);
}
}
iter.remove();
}
}
private ClusterPartition getPartition(Map partitions, String id) {
ClusterPartition partition = partitions.get(id);
if (partition == null) {
partition = new ClusterPartition(id);
partition.setType(Type.SLAVE);
partitions.put(id, partition);
}
return partition;
}
@Override
public void shutdown() {
if (monitorFuture != null) {
monitorFuture.cancel(true);
}
closeNodeConnections();
super.shutdown();
}
private Set getLastPartitions() {
return new HashSet(lastPartitions.values());
}
@Override
public URI getLastClusterNode() {
return lastClusterNode;
}
@Override
public boolean isClusterMode() {
return true;
}
}