use of voldemort.routing.StoreRoutingPlan in project voldemort by voldemort.
the class VoldemortAdminTool method executeShowRoutingPlan.
private static void executeShowRoutingPlan(AdminClient adminClient, String storeName, List<String> keyList) throws DecoderException {
Cluster cluster = adminClient.getAdminClientCluster();
List<StoreDefinition> storeDefs = adminClient.metadataMgmtOps.getRemoteStoreDefList().getValue();
StoreDefinition storeDef = StoreDefinitionUtils.getStoreDefinitionWithName(storeDefs, storeName);
StoreRoutingPlan routingPlan = new StoreRoutingPlan(cluster, storeDef);
BaseStoreRoutingPlan bRoutingPlan = new BaseStoreRoutingPlan(cluster, storeDef);
final int COLUMN_WIDTH = 30;
for (String keyStr : keyList) {
byte[] key = ByteUtils.fromHexString(keyStr);
System.out.println("Key :" + keyStr);
System.out.println("Replicating Partitions :" + routingPlan.getReplicatingPartitionList(key));
System.out.println("Replicating Nodes :");
List<Integer> nodeList = routingPlan.getReplicationNodeList(routingPlan.getMasterPartitionId(key));
for (int i = 0; i < nodeList.size(); i++) {
System.out.println(nodeList.get(i) + "\t" + cluster.getNodeById(nodeList.get(i)).getHost());
}
System.out.println("Zone Nary information :");
HashMap<Integer, Integer> zoneRepMap = storeDef.getZoneReplicationFactor();
for (Zone zone : cluster.getZones()) {
System.out.println("\tZone #" + zone.getId());
int numReplicas = -1;
if (zoneRepMap == null) {
// non zoned cluster
numReplicas = storeDef.getReplicationFactor();
} else {
// zoned cluster
if (!zoneRepMap.containsKey(zone.getId())) {
Utils.croak("Repfactor for Zone " + zone.getId() + " not found in storedef");
}
numReplicas = zoneRepMap.get(zone.getId());
}
String FormatString = "%s %s %s\n";
System.out.format(FormatString, Utils.paddedString("REPLICA#", COLUMN_WIDTH), Utils.paddedString("PARTITION", COLUMN_WIDTH), Utils.paddedString("NODE", COLUMN_WIDTH));
for (int i = 0; i < numReplicas; i++) {
Integer nodeId = bRoutingPlan.getNodeIdForZoneNary(zone.getId(), i, key);
Integer partitionId = routingPlan.getNodesPartitionIdForKey(nodeId, key);
System.out.format(FormatString, Utils.paddedString(i + "", COLUMN_WIDTH), Utils.paddedString(partitionId.toString(), COLUMN_WIDTH), Utils.paddedString(nodeId + "(" + cluster.getNodeById(nodeId).getHost() + ")", COLUMN_WIDTH));
}
System.out.println();
}
System.out.println("-----------------------------------------------");
System.out.println();
}
}
use of voldemort.routing.StoreRoutingPlan in project voldemort by voldemort.
the class PartitionBalanceUtils method analyzeInvalidMetadataRate.
// TODO: (refactor) separate analysis from pretty printing and add a unit
// test for the analysis sub-method.
/**
* Compares current cluster with final cluster. Uses pertinent store defs
* for each cluster to determine if a node that hosts a zone-primary in the
* current cluster will no longer host any zone-nary in the final cluster.
* This check is the precondition for a server returning an invalid metadata
* exception to a client on a normal-case put or get. Normal-case being that
* the zone-primary receives the pseudo-master put or the get operation.
*
* @param currentCluster
* @param currentStoreDefs
* @param finalCluster
* @param finalStoreDefs
* @return pretty-printed string documenting invalid metadata rates for each
* zone.
*/
public static String analyzeInvalidMetadataRate(final Cluster currentCluster, List<StoreDefinition> currentStoreDefs, final Cluster finalCluster, List<StoreDefinition> finalStoreDefs) {
StringBuilder sb = new StringBuilder();
sb.append("Dump of invalid metadata rates per zone").append(Utils.NEWLINE);
HashMap<StoreDefinition, Integer> uniqueStores = StoreDefinitionUtils.getUniqueStoreDefinitionsWithCounts(currentStoreDefs);
for (StoreDefinition currentStoreDef : uniqueStores.keySet()) {
sb.append("Store exemplar: " + currentStoreDef.getName()).append(Utils.NEWLINE).append("\tThere are " + uniqueStores.get(currentStoreDef) + " other similar stores.").append(Utils.NEWLINE);
StoreRoutingPlan currentSRP = new StoreRoutingPlan(currentCluster, currentStoreDef);
StoreDefinition finalStoreDef = StoreUtils.getStoreDef(finalStoreDefs, currentStoreDef.getName());
StoreRoutingPlan finalSRP = new StoreRoutingPlan(finalCluster, finalStoreDef);
// Only care about existing zones
for (int zoneId : currentCluster.getZoneIds()) {
int zonePrimariesCount = 0;
int invalidMetadata = 0;
// Examine nodes in current cluster in existing zone.
for (int nodeId : currentCluster.getNodeIdsInZone(zoneId)) {
// For every zone-primary in current cluster
for (int zonePrimaryPartitionId : currentSRP.getZonePrimaryPartitionIds(nodeId)) {
zonePrimariesCount++;
// InvalidMetadataException will fire.
if (!finalSRP.getZoneNAryPartitionIds(nodeId).contains(zonePrimaryPartitionId)) {
invalidMetadata++;
}
}
}
float rate = invalidMetadata / (float) zonePrimariesCount;
sb.append("\tZone " + zoneId).append(" : total zone primaries " + zonePrimariesCount).append(", # that trigger invalid metadata " + invalidMetadata).append(" => " + rate).append(Utils.NEWLINE);
}
}
return sb.toString();
}
use of voldemort.routing.StoreRoutingPlan in project voldemort by voldemort.
the class ClusterForkLiftTool method run.
@Override
public void run() {
final Cluster srcCluster = srcAdminClient.getAdminClientCluster();
try {
// process stores one-by-one
for (String store : storesList) {
logger.info("Processing store " + store);
dstStreamingClient.initStreamingSession(store, new Callable<Object>() {
@Override
public Object call() throws Exception {
return null;
}
}, new Callable<Object>() {
@Override
public Object call() throws Exception {
return null;
}
}, true);
final CountDownLatch latch = new CountDownLatch(srcCluster.getNumberOfPartitions());
StoreRoutingPlan storeInstance = new StoreRoutingPlan(srcCluster, srcStoreDefMap.get(store));
// submit work on every partition that is to be forklifted
for (Integer partitionId : partitionList) {
if (this.mode == ForkLiftTaskMode.global_resolution) {
// do thorough global resolution across replicas
SinglePartitionGloballyResolvingForkLiftTask work = new SinglePartitionGloballyResolvingForkLiftTask(storeInstance, partitionId, latch);
workerPool.submit(work);
} else if (this.mode == ForkLiftTaskMode.primary_resolution) {
// do the less cleaner, but much faster route
SinglePartitionPrimaryResolvingForkLiftTask work = new SinglePartitionPrimaryResolvingForkLiftTask(storeInstance, partitionId, latch);
workerPool.submit(work);
} else if (this.mode == ForkLiftTaskMode.no_resolution) {
// do the less cleaner, but much faster route
SinglePartitionNoResolutionForkLiftTask work = new SinglePartitionNoResolutionForkLiftTask(storeInstance, partitionId, latch);
workerPool.submit(work);
}
}
// wait till all the partitions are processed
latch.await();
dstStreamingClient.closeStreamingSession();
logger.info("Finished processing store " + store);
}
} catch (Exception e) {
logger.error("Exception running forklift tool", e);
} finally {
workerPool.shutdown();
try {
workerPool.awaitTermination(DEFAULT_WORKER_POOL_SHUTDOWN_WAIT_MINS, TimeUnit.MINUTES);
} catch (InterruptedException ie) {
logger.error("InterruptedException while waiting for worker pool to shutdown", ie);
}
srcAdminClient.close();
dstStreamingClient.getAdminClient().close();
}
}
use of voldemort.routing.StoreRoutingPlan in project voldemort by voldemort.
the class ZoneCLipperTest method testDropZoneIdWorks.
@Test
public void testDropZoneIdWorks() {
// Create a list of current partition ids. We will use this set to
// compare partitions ids with the interim cluster
Set<Integer> originalPartitions = new HashSet<Integer>();
for (Integer zoneId : initialCluster.getZoneIds()) {
originalPartitions.addAll(initialCluster.getPartitionIdsInZone(zoneId));
}
// Get an intermediate cluster where partitions that belong to the zone
// that is being dropped have been moved to the existing zones
Cluster interimCluster = RebalanceUtils.vacateZone(initialCluster, dropZoneId);
// Make sure that the intermediate cluster should have same number of
// partitions
RebalanceUtils.validateClusterPartitionCounts(initialCluster, interimCluster);
// Make sure that the intermediate cluster should have same number of
// nodes
RebalanceUtils.validateClusterNodeCounts(initialCluster, interimCluster);
// Make sure that the intermediate cluster doesn't have any partitions in
// the dropped zone
assertTrue("Zone being dropped has partitions. ZoneClipper didn't work properly", interimCluster.getPartitionIdsInZone(dropZoneId).isEmpty());
// Make sure that the nodes being dropped don't host any partitions
for (Integer nodeId : interimCluster.getNodeIdsInZone(dropZoneId)) {
assertTrue("Nodes in the zone being dropped don't have empty partitions list", interimCluster.getNodeById(nodeId).getPartitionIds().isEmpty());
}
Set<Integer> finalPartitions = new HashSet<Integer>();
for (Integer zoneId : interimCluster.getZoneIds()) {
finalPartitions.addAll(interimCluster.getPartitionIdsInZone(zoneId));
}
// Compare to original partition ids list
assertTrue("Original and interm partition ids don't match", originalPartitions.equals(finalPartitions));
// Make sure that there is no data movement
RebalancePlan rebalancePlan = ClusterTestUtils.makePlan(initialCluster, storeDefs, interimCluster, storeDefs);
// Make sure we have a plan
assertEquals(rebalancePlan.getPlan().size(), 1);
// Make sure there is no cross zones between zones in the plan
assertEquals(rebalancePlan.getPartitionStoresMovedXZone(), 0);
// Make sure there is no data movement between nodes
assertEquals(rebalancePlan.getPartitionStoresMoved(), 0);
for (Integer nodeId : interimCluster.getNodeIds()) {
Set<Integer> remainingNodes = Sets.symmetricDifference(interimCluster.getNodeIds(), Sets.newHashSet(nodeId));
for (Integer otherNodeId : remainingNodes) {
assertTrue("Something went wrong as there is data movement between nodes", rebalancePlan.getNodeMoveMap().get(nodeId, otherNodeId) == 0);
}
}
// Also get the adjusted store definitions, with the zone dropped
Cluster finalCluster = RebalanceUtils.dropZone(interimCluster, dropZoneId);
List<StoreDefinition> clippedStoreDefs = RebalanceUtils.dropZone(storeDefs, dropZoneId);
for (StoreDefinition storeDef : clippedStoreDefs) {
StoreDefinition orgStoreDef = StoreDefinitionUtils.getStoreDefinitionWithName(storeDefs, storeDef.getName());
assertFalse("Clipped storedef has replication for dropped zone", storeDef.getZoneReplicationFactor().containsKey(dropZoneId));
assertEquals("Clipped storedef has incorrect number of zones", initialCluster.getZones().size() - 1, storeDef.getZoneReplicationFactor().size());
assertEquals("Clipped storedef has incorrect total repfactor", orgStoreDef.getReplicationFactor() - orgStoreDef.getZoneReplicationFactor().get(dropZoneId), storeDef.getReplicationFactor());
}
// Confirm that we would not route to any of the dropped nodes for any
// store.
Set<Integer> dropNodes = interimCluster.getNodeIdsInZone(dropZoneId);
for (StoreDefinition storeDef : clippedStoreDefs) {
StoreRoutingPlan routingPlan = new StoreRoutingPlan(finalCluster, storeDef);
Node[] partitionToNode = finalCluster.getPartitionIdToNodeArray();
for (int p = 0; p < partitionToNode.length; p++) {
List<Integer> replicaNodes = routingPlan.getReplicationNodeList(p);
assertFalse("Should not be routing to any dropped nodes", replicaNodes.removeAll(dropNodes));
}
}
}
use of voldemort.routing.StoreRoutingPlan in project voldemort by voldemort.
the class ClusterForkLiftToolTest method testForkLiftOverWrite.
@Test
public void testForkLiftOverWrite() throws Exception {
StoreRoutingPlan srcStoreInstance = new StoreRoutingPlan(srcCluster, globallyResolvingStoreDef);
// populate data on the source cluster..
for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
srcGloballyResolvingStoreClient.put(entry.getKey(), entry.getValue());
}
// generate a conflict on the primary and a secondary
List<Integer> nodeList = srcStoreInstance.getReplicationNodeList(srcStoreInstance.getMasterPartitionId(conflictKey.getBytes("UTF-8")));
VectorClock losingClock = new VectorClock(Lists.newArrayList(new ClockEntry((short) 0, 5)), System.currentTimeMillis());
VectorClock winningClock = new VectorClock(Lists.newArrayList(new ClockEntry((short) 1, 5)), losingClock.getTimestamp() + 1);
srcAdminClient.storeOps.putNodeKeyValue(GLOBALLY_RESOLVING_STORE_NAME, new NodeValue<ByteArray, byte[]>(nodeList.get(0), new ByteArray(conflictKey.getBytes("UTF-8")), new Versioned<byte[]>("losing value".getBytes("UTF-8"), losingClock)));
srcAdminClient.storeOps.putNodeKeyValue(GLOBALLY_RESOLVING_STORE_NAME, new NodeValue<ByteArray, byte[]>(nodeList.get(1), new ByteArray(conflictKey.getBytes("UTF-8")), new Versioned<byte[]>("winning value".getBytes("UTF-8"), winningClock)));
// *** do a write to destination cluster ***
// This is the main test , where after fork lift this value should be
// overwritten. This is the only difference.
dstGloballyResolvingStoreClient.put(firstKey, "before forklift");
// Make the current thread sleep , so when the new clock is generated
// using milliSeconds it is greater and must be overwritten.
Thread.sleep(2);
// perform the forklifting..
ClusterForkLiftTool forkLiftTool = new ClusterForkLiftTool(srcBootStrapUrl, dstBootStrapUrl, // OverWrite
true, // ignore
false, // mismatch
10000, 1, 1000, Lists.newArrayList(GLOBALLY_RESOLVING_STORE_NAME), null, ClusterForkLiftTool.ForkLiftTaskMode.global_resolution);
forkLiftTool.run();
// do a write to destination cluster
dstGloballyResolvingStoreClient.put(lastKey, "after forklift");
// verify data on the destination is as expected
for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
String dstClusterValue = dstGloballyResolvingStoreClient.get(entry.getKey()).getValue();
if (entry.getKey().equals(lastKey)) {
assertEquals("can't update value after forklift", dstClusterValue, "after forklift");
} else if (entry.getKey().equals(conflictKey)) {
assertEquals("Conflict resolution incorrect", dstClusterValue, "winning value");
} else {
if (!dstClusterValue.equals(entry.getValue())) {
assertEquals("fork lift data missing", dstClusterValue, entry.getValue());
}
}
}
}
Aggregations