/*
* Copyright 2013 LinkedIn, Inc
*
* 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 voldemort.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* UpdateClusterUtils provides helper methods for manipulating nodes and
* clusters. The methods are used by rebalancing tools and in tests.
*/
public class UpdateClusterUtils {
private static Logger logger = Logger.getLogger(UpdateClusterUtils.class);
/**
* Creates a replica of the node with the new partitions list
*
* @param node The node whose replica we are creating
* @param partitionsList The new partitions list
* @return Replica of node with new partitions list
*/
public static Node updateNode(Node node, List<Integer> partitionsList) {
return new Node(node.getId(),
node.getHost(),
node.getHttpPort(),
node.getSocketPort(),
node.getAdminPort(),
node.getZoneId(),
partitionsList);
}
/**
* Add a partition to the node provided
*
* @param node The node to which we'll add the partition
* @param donatedPartition The partition to add
* @return The new node with the new partition
*/
public static Node addPartitionToNode(final Node node, Integer donatedPartition) {
return UpdateClusterUtils.addPartitionsToNode(node, Sets.newHashSet(donatedPartition));
}
/**
* Remove a partition from the node provided
*
* @param node The node from which we're removing the partition
* @param donatedPartition The partitions to remove
* @return The new node without the partition
*/
public static Node removePartitionFromNode(final Node node, Integer donatedPartition) {
return UpdateClusterUtils.removePartitionsFromNode(node, Sets.newHashSet(donatedPartition));
}
/**
* Add the set of partitions to the node provided
*
* @param node The node to which we'll add the partitions
* @param donatedPartitions The list of partitions to add
* @return The new node with the new partitions
*/
public static Node addPartitionsToNode(final Node node, final Set<Integer> donatedPartitions) {
List<Integer> deepCopy = new ArrayList<Integer>(node.getPartitionIds());
deepCopy.addAll(donatedPartitions);
Collections.sort(deepCopy);
return updateNode(node, deepCopy);
}
/**
* Remove the set of partitions from the node provided
*
* @param node The node from which we're removing the partitions
* @param donatedPartitions The list of partitions to remove
* @return The new node without the partitions
*/
public static Node removePartitionsFromNode(final Node node,
final Set<Integer> donatedPartitions) {
List<Integer> deepCopy = new ArrayList<Integer>(node.getPartitionIds());
deepCopy.removeAll(donatedPartitions);
return updateNode(node, deepCopy);
}
/**
* Concatenates the list of current nodes in the given cluster with the new
* nodes provided and returns an updated cluster metadata. <br>
* If the nodes being updated already exist in the current metadata, we take
* the updated ones
*
* @param currentCluster The current cluster metadata
* @param updatedNodeList The list of new nodes to be added
* @return New cluster metadata containing both the sets of nodes
*/
public static Cluster updateCluster(Cluster currentCluster, List<Node> updatedNodeList) {
List<Node> newNodeList = new ArrayList<Node>(updatedNodeList);
for(Node currentNode: currentCluster.getNodes()) {
if(!updatedNodeList.contains(currentNode))
newNodeList.add(currentNode);
}
Collections.sort(newNodeList);
return new Cluster(currentCluster.getName(),
newNodeList,
Lists.newArrayList(currentCluster.getZones()));
}
/**
* Updates the existing cluster such that we remove partitions mentioned
* from the stealer node and add them to the donor node
*
* @param currentCluster Existing cluster metadata. Both stealer and donor
* node should already exist in this metadata
* @param stealerNodeId Id of node for which we are stealing the partitions
* @param donatedPartitions List of partitions we are moving
* @return Updated cluster metadata
*/
public static Cluster createUpdatedCluster(Cluster currentCluster,
int stealerNodeId,
List<Integer> donatedPartitions) {
Cluster updatedCluster = Cluster.cloneCluster(currentCluster);
// Go over every donated partition one by one
for(int donatedPartition: donatedPartitions) {
// Gets the donor Node that owns this donated partition
Node donorNode = updatedCluster.getNodeForPartitionId(donatedPartition);
Node stealerNode = updatedCluster.getNodeById(stealerNodeId);
if(donorNode == stealerNode) {
// Moving to the same location = No-op
continue;
}
// Update the list of partitions for this node
donorNode = removePartitionFromNode(donorNode, donatedPartition);
stealerNode = addPartitionToNode(stealerNode, donatedPartition);
// Sort the nodes
updatedCluster = updateCluster(updatedCluster,
Lists.newArrayList(donorNode, stealerNode));
}
return updatedCluster;
}
}