/*
* Copyright 2008-2009 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.routing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.utils.FnvHashFunction;
import voldemort.utils.HashFunction;
/**
* A Zone Routing strategy that sits on top of the Consistent Routing strategy
* such that each request goes to zone specific replicas
*
*/
public class ZoneRoutingStrategy extends ConsistentRoutingStrategy {
private HashMap<Integer, Integer> zoneReplicationFactor;
public ZoneRoutingStrategy(Cluster cluster,
HashMap<Integer, Integer> zoneReplicationFactor,
int numReplicas) {
this(new FnvHashFunction(), cluster, zoneReplicationFactor, numReplicas);
}
public ZoneRoutingStrategy(HashFunction hash,
Cluster cluster,
HashMap<Integer, Integer> zoneReplicationFactor,
int numReplicas) {
super(hash, cluster, numReplicas);
this.zoneReplicationFactor = zoneReplicationFactor;
}
/**
* Get the replication partitions list for the given partition.
*
* @param index Partition id for which we are generating the preference list
* @return The List of partitionId where this partition is replicated.
*/
@Override
public List<Integer> getReplicatingPartitionList(int index) {
List<Node> preferenceNodesList = new ArrayList<Node>(getNumReplicas());
List<Integer> replicationPartitionsList = new ArrayList<Integer>(getNumReplicas());
// Copy Zone based Replication Factor
HashMap<Integer, Integer> requiredRepFactor = new HashMap<Integer, Integer>();
requiredRepFactor.putAll(zoneReplicationFactor);
// Cross-check if individual zone replication factor equals global
int sum = 0;
for(Integer zoneRepFactor: requiredRepFactor.values()) {
sum += zoneRepFactor;
}
if(sum != getNumReplicas())
throw new IllegalArgumentException("Number of zone replicas is not equal to the total replication factor");
if(getPartitionToNode().length == 0) {
return new ArrayList<Integer>(0);
}
for(int i = 0; i < getPartitionToNode().length; i++) {
// add this one if we haven't already, and it can satisfy some zone
// replicationFactor
Node currentNode = getNodeByPartition(index);
if(!preferenceNodesList.contains(currentNode)) {
preferenceNodesList.add(currentNode);
if(checkZoneRequirement(requiredRepFactor, currentNode.getZoneId()))
replicationPartitionsList.add(index);
}
// if we have enough, go home
if(replicationPartitionsList.size() >= getNumReplicas())
return replicationPartitionsList;
// move to next clockwise slot on the ring
index = (index + 1) % getPartitionToNode().length;
}
// we don't have enough, but that may be okay
return replicationPartitionsList;
}
/**
* Check if we still need more nodes from the given zone and reduce the
* zoneReplicationFactor count accordingly.
*
* @param requiredRepFactor
* @param zoneId
* @return
*/
private boolean checkZoneRequirement(HashMap<Integer, Integer> requiredRepFactor, int zoneId) {
if(requiredRepFactor.containsKey(zoneId)) {
if(requiredRepFactor.get(zoneId) == 0) {
return false;
} else {
requiredRepFactor.put(zoneId, requiredRepFactor.get(zoneId) - 1);
return true;
}
}
return false;
}
@Override
public String getType() {
return RoutingStrategyType.ZONE_STRATEGY;
}
}