private void checkSlotsMigration(Collection<ClusterPartition> newPartitions) {
    List<ClusterPartition> currentPartitions =
        new ArrayList<ClusterPartition>(lastPartitions.values());
    for (ClusterPartition currentPartition : currentPartitions) {
      for (ClusterPartition newPartition : newPartitions) {
        if (!currentPartition.getNodeId().equals(newPartition.getNodeId())) {
          continue;
        }

        Set<ClusterSlotRange> addedSlots =
            new HashSet<ClusterSlotRange>(newPartition.getSlotRanges());
        addedSlots.removeAll(currentPartition.getSlotRanges());
        MasterSlaveEntry entry = getEntry(currentPartition.getSlotRanges().iterator().next());
        currentPartition.addSlotRanges(addedSlots);
        for (ClusterSlotRange slot : addedSlots) {
          entry.addSlotRange(slot);
          addEntry(slot, entry);
          log.info("{} slot added for {}", slot, entry.getClient().getAddr());
          lastPartitions.put(slot, currentPartition);
        }

        Set<ClusterSlotRange> removedSlots =
            new HashSet<ClusterSlotRange>(currentPartition.getSlotRanges());
        removedSlots.removeAll(newPartition.getSlotRanges());
        lastPartitions.keySet().removeAll(removedSlots);
        currentPartition.removeSlotRanges(removedSlots);

        for (ClusterSlotRange slot : removedSlots) {
          log.info("{} slot removed for {}", slot, entry.getClient().getAddr());
          entry.removeSlotRange(slot);
          removeMaster(slot);
        }
      }
    }
  }
  private void addRemoveSlaves(
      final MasterSlaveEntry entry,
      final ClusterPartition currentPart,
      final ClusterPartition newPart) {
    Set<URI> removedSlaves = new HashSet<URI>(currentPart.getSlaveAddresses());
    removedSlaves.removeAll(newPart.getSlaveAddresses());

    for (URI uri : removedSlaves) {
      currentPart.removeSlaveAddress(uri);

      slaveDown(entry, uri.getHost(), uri.getPort(), FreezeReason.MANAGER);
      log.info("slave {} removed for slot ranges: {}", uri, currentPart.getSlotRanges());
    }

    Set<URI> addedSlaves = new HashSet<URI>(newPart.getSlaveAddresses());
    addedSlaves.removeAll(currentPart.getSlaveAddresses());
    for (final URI uri : addedSlaves) {
      Future<Void> future = entry.addSlave(uri.getHost(), uri.getPort());
      future.addListener(
          new FutureListener<Void>() {
            @Override
            public void operationComplete(Future<Void> future) throws Exception {
              if (!future.isSuccess()) {
                log.error("Can't add slave: " + uri, future.cause());
                return;
              }

              currentPart.addSlaveAddress(uri);
              entry.slaveUp(uri.getHost(), uri.getPort(), FreezeReason.MANAGER);
              log.info("slave {} added for slot ranges: {}", uri, currentPart.getSlotRanges());
            }
          });
    }
  }
  private void checkSlotsChange(
      ClusterServersConfig cfg, Collection<ClusterPartition> newPartitions) {
    checkSlotsMigration(newPartitions);

    Collection<ClusterSlotRange> newPartitionsSlots = slots(newPartitions);
    Set<ClusterSlotRange> removedSlots = new HashSet<ClusterSlotRange>(lastPartitions.keySet());
    removedSlots.removeAll(newPartitionsSlots);
    lastPartitions.keySet().removeAll(removedSlots);
    if (!removedSlots.isEmpty()) {
      log.info("{} slot ranges found to remove", removedSlots);
    }

    for (ClusterSlotRange slot : removedSlots) {
      MasterSlaveEntry entry = removeMaster(slot);
      entry.removeSlotRange(slot);
      if (entry.getSlotRanges().isEmpty()) {
        entry.shutdownMasterAsync();
        log.info("{} master and slaves for it removed", entry.getClient().getAddr());
      }
    }

    Set<ClusterSlotRange> addedSlots = new HashSet<ClusterSlotRange>(newPartitionsSlots);
    addedSlots.removeAll(lastPartitions.keySet());
    if (!addedSlots.isEmpty()) {
      log.info("{} slots found to add", addedSlots);
    }
    for (final ClusterSlotRange slot : addedSlots) {
      ClusterPartition partition = find(newPartitions, slot);
      boolean masterFound = false;
      for (MasterSlaveEntry entry : getEntries().values()) {
        if (entry.getClient().getAddr().equals(partition.getMasterAddr())) {
          addEntry(slot, entry);
          lastPartitions.put(slot, partition);
          masterFound = true;
          break;
        }
      }
      if (!masterFound) {
        Future<Collection<Future<Void>>> future = addMasterEntry(partition, cfg);
        future.addListener(
            new FutureListener<Collection<Future<Void>>>() {
              @Override
              public void operationComplete(Future<Collection<Future<Void>>> future)
                  throws Exception {
                if (!future.isSuccess()) {
                  log.error(
                      "New cluster slot range " + slot + " without master node detected",
                      future.cause());
                }
              }
            });
      }
    }
  }
  private void upDownSlaves(
      final MasterSlaveEntry entry,
      final ClusterPartition currentPart,
      final ClusterPartition newPart) {
    Set<URI> aliveSlaves = new HashSet<URI>(currentPart.getFailedSlaveAddresses());
    aliveSlaves.removeAll(newPart.getFailedSlaveAddresses());
    for (URI uri : aliveSlaves) {
      currentPart.removeFailedSlaveAddress(uri);
      if (entry.slaveUp(uri.getHost(), uri.getPort(), FreezeReason.MANAGER)) {
        log.info("slave: {} has up for slot ranges: {}", uri, currentPart.getSlotRanges());
      }
    }

    Set<URI> failedSlaves = new HashSet<URI>(newPart.getFailedSlaveAddresses());
    failedSlaves.removeAll(currentPart.getFailedSlaveAddresses());
    for (URI uri : failedSlaves) {
      currentPart.addFailedSlaveAddress(uri);
      slaveDown(entry, uri.getHost(), uri.getPort(), FreezeReason.MANAGER);
      log.warn("slave: {} has down for slot ranges: {}", uri, currentPart.getSlotRanges());
    }
  }
 private void checkForReconnect(ClientConnectionsEntry entry) {
   if (entry.getNodeType() == NodeType.SLAVE) {
     masterSlaveEntry.slaveDown(
         entry.getClient().getAddr().getHostName(),
         entry.getClient().getAddr().getPort(),
         FreezeReason.RECONNECT);
     log.warn(
         "slave {} disconnected due to failedAttempts={} limit reached",
         entry.getClient().getAddr(),
         config.getFailedAttempts());
     scheduleCheck(entry);
   } else {
     if (entry.freezeMaster(FreezeReason.RECONNECT)) {
       log.warn(
           "host {} disconnected due to failedAttempts={} limit reached",
           entry.getClient().getAddr(),
           config.getFailedAttempts());
       scheduleCheck(entry);
     }
   }
 }
  public Future<T> get() {
    for (int j = entries.size() - 1; j >= 0; j--) {
      ClientConnectionsEntry entry = getEntry();
      if (!entry.isFreezed() && tryAcquireConnection(entry)) {
        return connectTo(entry);
      }
    }

    List<InetSocketAddress> zeroConnectionsAmount = new LinkedList<InetSocketAddress>();
    List<InetSocketAddress> freezed = new LinkedList<InetSocketAddress>();
    for (ClientConnectionsEntry entry : entries) {
      if (entry.isFreezed()) {
        freezed.add(entry.getClient().getAddr());
      } else {
        zeroConnectionsAmount.add(entry.getClient().getAddr());
      }
    }

    StringBuilder errorMsg;
    if (connectionManager.isClusterMode()) {
      errorMsg =
          new StringBuilder(
              "Connection pool exhausted! for slots: " + masterSlaveEntry.getSlotRanges());
    } else {
      errorMsg = new StringBuilder("Connection pool exhausted! ");
    }
    if (!freezed.isEmpty()) {
      errorMsg.append(" Disconnected hosts: " + freezed);
    }
    if (!zeroConnectionsAmount.isEmpty()) {
      errorMsg.append(" Hosts with fully busy connections: " + zeroConnectionsAmount);
    }

    RedisConnectionException exception = new RedisConnectionException(errorMsg.toString());
    return connectionManager.newFailedFuture(exception);
  }