blob: fef1371c937f5ae5ff4a0081236066491bac9400 [file] [log] [blame]
package org.apache.helix.controller.rebalancer.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.util.HashMap;
import java.util.Map;
import org.apache.helix.api.rebalancer.constraint.dataprovider.PartitionWeightProvider;
import org.apache.helix.controller.common.ResourcesStateMap;
import org.apache.helix.model.Partition;
import org.apache.helix.model.ResourceAssignment;
public class ResourceUsageCalculator {
/**
* A convenient tool for calculating partition capacity usage based on the assignment and resource weight provider.
*
* @param resourceAssignment
* @param weightProvider
* @return
*/
public static Map<String, Integer> getResourceUsage(ResourcesStateMap resourceAssignment,
PartitionWeightProvider weightProvider) {
Map<String, Integer> newParticipantUsage = new HashMap<>();
for (String resource : resourceAssignment.resourceSet()) {
Map<Partition, Map<String, String>> stateMap =
resourceAssignment.getPartitionStateMap(resource).getStateMap();
for (Partition partition : stateMap.keySet()) {
for (String participant : stateMap.get(partition).keySet()) {
if (!newParticipantUsage.containsKey(participant)) {
newParticipantUsage.put(participant, 0);
}
newParticipantUsage.put(participant, newParticipantUsage.get(participant) + weightProvider
.getPartitionWeight(resource, partition.getPartitionName()));
}
}
}
return newParticipantUsage;
}
/**
* Measure baseline divergence between baseline assignment and best possible assignment at
* replica level. Example as below:
* baseline =
* {
* resource1={
* partition1={
* instance1=master,
* instance2=slave
* },
* partition2={
* instance2=slave
* }
* }
* }
* bestPossible =
* {
* resource1={
* partition1={
* instance1=master, <--- matched
* instance3=slave <--- doesn't match
* },
* partition2={
* instance3=master <--- doesn't match
* }
* }
* }
* baseline divergence = (matched: 1) / (doesn't match: 3) = 1/3 ~= 0.333
* @param baseline baseline assignment
* @param bestPossibleAssignment best possible assignment
* @return double value range at [0.0, 1.0]
*/
public static double measureBaselineDivergence(Map<String, ResourceAssignment> baseline,
Map<String, ResourceAssignment> bestPossibleAssignment) {
int numMatchedReplicas = 0;
int numTotalBestPossibleReplicas = 0;
// 1. Check resource assignment names.
for (Map.Entry<String, ResourceAssignment> resourceEntry : bestPossibleAssignment.entrySet()) {
String resourceKey = resourceEntry.getKey();
if (!baseline.containsKey(resourceKey)) {
continue;
}
// Resource assignment names are matched.
// 2. check partitions.
Map<String, Map<String, String>> bestPossiblePartitions =
resourceEntry.getValue().getRecord().getMapFields();
Map<String, Map<String, String>> baselinePartitions =
baseline.get(resourceKey).getRecord().getMapFields();
for (Map.Entry<String, Map<String, String>> partitionEntry
: bestPossiblePartitions.entrySet()) {
String partitionName = partitionEntry.getKey();
if (!baselinePartitions.containsKey(partitionName)) {
continue;
}
// Partition names are matched.
// 3. Check replicas.
Map<String, String> bestPossibleReplicas = partitionEntry.getValue();
Map<String, String> baselineReplicas = baselinePartitions.get(partitionName);
for (Map.Entry<String, String> replicaEntry : bestPossibleReplicas.entrySet()) {
String replicaName = replicaEntry.getKey();
if (!baselineReplicas.containsKey(replicaName)) {
continue;
}
// Replica names are matched.
// 4. Check replica values.
String bestPossibleReplica = replicaEntry.getValue();
String baselineReplica = baselineReplicas.get(replicaName);
if (bestPossibleReplica.equals(baselineReplica)) {
numMatchedReplicas++;
}
}
// Count total best possible replicas.
numTotalBestPossibleReplicas += bestPossibleReplicas.size();
}
}
return numTotalBestPossibleReplicas == 0 ? 0.0d
: (double) numMatchedReplicas / (double) numTotalBestPossibleReplicas;
}
}