/* * 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.routing; import java.util.List; import voldemort.VoldemortException; import voldemort.cluster.Cluster; import voldemort.cluster.Node; import voldemort.store.StoreDefinition; import voldemort.utils.ByteUtils; import voldemort.utils.Utils; /** * This class wraps up a Cluster object and a StoreDefinition. The methods are * effectively helper or util style methods for querying the routing plan that * will be generated for a given routing strategy upon store and cluster * topology information. * * This object may be constructed in the fast path (e.g., during proxy'ing) and * so this object must be fast/simple to construct. Invocations of getters to * find replica lists and n-aries can be O(number of replicas). * * The intermingling of key-based interfaces and partition-based interfaces is * ugly in this class. Partition-based interfaces should be in an underlying * class and then key-based interfaces should wrap those up. Or, all of these * types of class should implement the getMasterPartition(byte[]) interface from * RoutingStrategy. Then the caller always does translation from key to * partition ID, and the *StoreRouting* classes only offer partition-based * interfaces. */ // TODO: Should find a better name for this class. Need to do same for what is // currently StoreRoutingPlan. Suggestions thus far for this class: // ProxyRoutingPlan, ProxyStoreRouter, SimpleStoreRouter, ... public class BaseStoreRoutingPlan { private final Cluster cluster; private final StoreDefinition storeDefinition; private final RoutingStrategy routingStrategy; public BaseStoreRoutingPlan(Cluster cluster, StoreDefinition storeDefinition) { this.cluster = cluster; this.storeDefinition = storeDefinition; this.routingStrategy = new RoutingStrategyFactory().updateRoutingStrategy(storeDefinition, cluster); } public Cluster getCluster() { return cluster; } public StoreDefinition getStoreDefinition() { return storeDefinition; } /** * Determines master partition ID for the key. * * @param key * @return master partition id */ public int getMasterPartitionId(final byte[] key) { return this.routingStrategy.getMasterPartition(key); } /** * Given a key that belong to a given node, returns a number n (< zone * replication factor), such that the given node holds the key as the nth * replica of the given zone * * eg: if the method returns 1, then given node hosts the key as the zone * secondary in the given zone * * @param zoneId * @param nodeId * @param key * @return zone n-ary level for key hosted on node id in zone id. */ // TODO: add unit test. public int getZoneNAry(int zoneId, int nodeId, byte[] key) { if(cluster.getNodeById(nodeId).getZoneId() != zoneId) { throw new VoldemortException("Node " + nodeId + " is not in zone " + zoneId + "! The node is in zone " + cluster.getNodeById(nodeId).getZoneId()); } List<Node> replicatingNodes = this.routingStrategy.routeRequest(key); int zoneNAry = -1; for(Node node: replicatingNodes) { // bump up the replica number once you encounter a node in the given // zone if(node.getZoneId() == zoneId) { zoneNAry++; } // we are done when we find the given node if(node.getId() == nodeId) { return zoneNAry; } } if(zoneNAry > -1) { throw new VoldemortException("Node " + nodeId + " not a replica for the key " + ByteUtils.toHexString(key) + " in given zone " + zoneId); } else { throw new VoldemortException("Could not find any replicas for the key " + ByteUtils.toHexString(key) + " in given zone " + zoneId); } } /** * Given a key and a zoneNary (< zone replication factor), figure out the * node that contains the key as the nth replica in the given zone. * * @param zoneId * @param zoneNary * @param key * @return node id that hosts zone n-ary replica for the key */ // TODO: add unit test. public int getNodeIdForZoneNary(int zoneId, int zoneNary, byte[] key) { List<Node> replicatingNodes = this.routingStrategy.routeRequest(key); int zoneNAry = -1; for(Node node: replicatingNodes) { // bump up the counter if we encounter a replica in the given zone; // return current node if counter now matches requested if(node.getZoneId() == zoneId) { zoneNAry++; if(zoneNAry == zoneNary) { return node.getId(); } } } if(zoneNAry == -1) { throw new VoldemortException("Could not find any replicas for the key " + ByteUtils.toHexString(key) + " in given zone " + zoneId); } else { throw new VoldemortException("Could not find " + (zoneNary + 1) + " replicas for the key " + ByteUtils.toHexString(key) + " in given zone " + zoneId + ". Only found " + (zoneNAry + 1)); } } /** * Determines the list of nodes that the key replicates to * * @param key * @return list of nodes that key replicates to */ public List<Integer> getReplicationNodeList(final byte[] key) { return Utils.nodeListToNodeIdList(this.routingStrategy.routeRequest(key)); } }