/*
* Copyright 2008-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;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.routing.RoutingStrategy;
import voldemort.routing.RoutingStrategyFactory;
import voldemort.store.StoreDefinition;
import voldemort.utils.Pair;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/*
* This class contains utils methods related to RO (read-only) stores. Historically,
* a structure named "replica_type" existed that was unfortunately hard coded into the file names
* on disk for the RO stores. Later, when almost all of the code base was refactored to remove
* replica_type this last piece of code that still had the notion of replica_type tied to it
* could not be removed.
*
* The following two methods are called only from test classes and are exclusively related to
* the RO store.
*
*/
public class ROTestUtils {
private static Logger logger = Logger.getLogger(ROTestUtils.class);
/**
* Given a list of tuples of [replica_type, partition], flattens it and
* generates a map of replica_type to partition mapping
*
* @param partitionTuples Set of <replica_type, partition> tuples
* @return Map of replica_type to set of partitions
*/
public static HashMap<Integer, List<Integer>> flattenPartitionTuples(Set<Pair<Integer, Integer>> partitionTuples) {
HashMap<Integer, List<Integer>> flattenedTuples = Maps.newHashMap();
for(Pair<Integer, Integer> pair: partitionTuples) {
if(flattenedTuples.containsKey(pair.getFirst())) {
flattenedTuples.get(pair.getFirst()).add(pair.getSecond());
} else {
List<Integer> newPartitions = Lists.newArrayList();
newPartitions.add(pair.getSecond());
flattenedTuples.put(pair.getFirst(), newPartitions);
}
}
return flattenedTuples;
}
/**
* For a particular cluster creates a mapping of node id to their
* corresponding list of [ replicaType, partition ] tuple
*
* @param cluster The cluster metadata
* @param storeDef The store definition
* @param includePrimary Include the primary partition?
* @return Map of node id to set of [ replicaType, partition ] tuple
*/
public static Map<Integer, Set<Pair<Integer, Integer>>> getNodeIdToAllPartitions(final Cluster cluster,
final StoreDefinition storeDef,
boolean includePrimary) {
final RoutingStrategy routingStrategy = new RoutingStrategyFactory().updateRoutingStrategy(storeDef,
cluster);
final Map<Integer, Set<Pair<Integer, Integer>>> nodeIdToReplicas = new HashMap<Integer, Set<Pair<Integer, Integer>>>();
final Map<Integer, Integer> partitionToNodeIdMap = cluster.getPartitionIdToNodeIdMap();
// Map initialization.
for(Node node: cluster.getNodes()) {
nodeIdToReplicas.put(node.getId(), new HashSet<Pair<Integer, Integer>>());
}
// Track how many zones actually have partitions (and so replica types)
// in them.
int zonesWithPartitions = 0;
for(Integer zoneId: cluster.getZoneIds()) {
if(cluster.getNumberOfPartitionsInZone(zoneId) > 0) {
zonesWithPartitions++;
}
}
// Loops through all nodes
for(Node node: cluster.getNodes()) {
// Gets the partitions that this node was configured with.
for(Integer primary: node.getPartitionIds()) {
// Gets the list of replicating partitions.
List<Integer> replicaPartitionList = routingStrategy.getReplicatingPartitionList(primary);
if((replicaPartitionList.size() % zonesWithPartitions != 0)
|| ((replicaPartitionList.size() / zonesWithPartitions) != (storeDef.getReplicationFactor() / cluster.getNumberOfZones()))) {
// For zone expansion & shrinking, this warning is expected
// in some cases. For other use cases (shuffling, cluster
// expansion), this warning indicates that something
// is wrong between the clusters and store defs.
logger.warn("Number of replicas returned (" + replicaPartitionList.size()
+ ") does not make sense given the replication factor ("
+ storeDef.getReplicationFactor() + ") and that there are "
+ cluster.getNumberOfZones() + " zones of which "
+ zonesWithPartitions + " have partitions (and of which "
+ (cluster.getNumberOfZones() - zonesWithPartitions)
+ " are empty).");
}
int replicaType = 0;
if(!includePrimary) {
replicaPartitionList.remove(primary);
replicaType = 1;
}
// Get the node that this replicating partition belongs to.
for(Integer replicaPartition: replicaPartitionList) {
Integer replicaNodeId = partitionToNodeIdMap.get(replicaPartition);
// The replicating node will have a copy of primary.
nodeIdToReplicas.get(replicaNodeId).add(Pair.create(replicaType, primary));
replicaType++;
}
}
}
return nodeIdToReplicas;
}
}