/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* 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 com.linkedin.pinot.routing.builder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.apache.helix.model.ExternalView;
/**
* Utility class to share common utility methods between routing table builders.
*/
public abstract class AbstractRoutingTableBuilder implements RoutingTableBuilder {
/**
* Picks a random replica, inversely weighted by the number of segments already assigned to each replica. See a longer
* description of the probabilities used in
* {@link KafkaLowLevelConsumerRoutingTableBuilder#computeRoutingTableFromExternalView(String, ExternalView, List)}
*
* @param validReplicaSet The list of valid replicas from which to pick a replica
* @param instanceToSegmentSetMap A map containing the segments already assigned to each replica for routing
* @param random The random number generator to use
* @return The instance name of a replica that was chosen
*/
protected String pickWeightedRandomReplica(Set<String> validReplicaSet,
Map<String, Set<String>> instanceToSegmentSetMap, Random random) {
// No replicas?
if (validReplicaSet.isEmpty()) {
return null;
}
// Only one valid replica?
if (validReplicaSet.size() == 1) {
return validReplicaSet.iterator().next();
}
// Find maximum segment count assigned to a replica
String[] replicas = validReplicaSet.toArray(new String[validReplicaSet.size()]);
int[] replicaSegmentCounts = new int[validReplicaSet.size()];
int maxSegmentCount = 0;
for (int i = 0; i < replicas.length; i++) {
String replica = replicas[i];
int replicaSegmentCount = 0;
if (instanceToSegmentSetMap.containsKey(replica)) {
replicaSegmentCount = instanceToSegmentSetMap.get(replica).size();
}
replicaSegmentCounts[i] = replicaSegmentCount;
if (maxSegmentCount < replicaSegmentCount) {
maxSegmentCount = replicaSegmentCount;
}
}
// Compute replica weights
int[] replicaWeights = new int[validReplicaSet.size()];
int totalReplicaWeights = 0;
for (int i = 0; i < replicas.length; i++) {
int replicaWeight = maxSegmentCount - replicaSegmentCounts[i];
replicaWeights[i] = replicaWeight;
totalReplicaWeights += replicaWeight;
}
// If all replicas are equal, just pick a random replica
if (totalReplicaWeights == 0) {
return replicas[random.nextInt(replicas.length)];
}
// Pick the proper replica given their respective weights
int randomValue = random.nextInt(totalReplicaWeights);
int i = 0;
while(replicaWeights[i] == 0 || replicaWeights[i] <= randomValue) {
randomValue -= replicaWeights[i];
++i;
}
return replicas[i];
}
}