use of org.apache.pulsar.broker.loadbalance.ResourceUnit in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method doLoadRanking.
/**
* Rank brokers by available capacity, or load percentage, based on placement strategy:
*
* - Available capacity for weighted random selection (weightedRandomSelection): ranks ResourceUnits units based on
* estimation of their capacity which is basically how many bundles each ResourceUnit is able can handle with its
* available resources (CPU, memory, network, etc);
*
* - Load percentage for least loaded server (leastLoadedServer): ranks ResourceUnits units based on estimation of
* their load percentage which is basically how many percent of resource is allocated which is
* max(resource_actually_used, resource_quota)
*
* If we fail to collect the Load Reports OR fail to process them for the first time, it means the leader does not
* have enough information to make a decision so we set it to ready when we collect and process the load reports
* successfully the first time.
*/
private synchronized void doLoadRanking() {
ResourceUnitRanking.setCpuUsageByMsgRate(this.realtimeCpuLoadFactor);
String hostname = pulsar.getAdvertisedAddress();
String strategy = this.getLoadBalancerPlacementStrategy();
log.info("doLoadRanking - load balancing strategy: {}", strategy);
if (!currentLoadReports.isEmpty()) {
Map<Long, Set<ResourceUnit>> newSortedRankings = Maps.newTreeMap();
Map<ResourceUnit, ResourceUnitRanking> newResourceUnitRankings = new HashMap<>();
for (Map.Entry<ResourceUnit, LoadReport> entry : currentLoadReports.entrySet()) {
ResourceUnit resourceUnit = entry.getKey();
LoadReport loadReport = entry.getValue();
// calculate rankings
Set<String> loadedBundles = loadReport.getBundles();
Set<String> preAllocatedBundles = null;
if (resourceUnitRankings.containsKey(resourceUnit)) {
preAllocatedBundles = resourceUnitRankings.get(resourceUnit).getPreAllocatedBundles();
preAllocatedBundles.removeAll(loadedBundles);
} else {
preAllocatedBundles = new HashSet<>();
}
ResourceQuota allocatedQuota = getTotalAllocatedQuota(loadedBundles);
ResourceQuota preAllocatedQuota = getTotalAllocatedQuota(preAllocatedBundles);
ResourceUnitRanking ranking = new ResourceUnitRanking(loadReport.getSystemResourceUsage(), loadedBundles, allocatedQuota, preAllocatedBundles, preAllocatedQuota);
newResourceUnitRankings.put(resourceUnit, ranking);
// generated sorted ranking
double loadPercentage = ranking.getEstimatedLoadPercentage();
long maxCapacity = ranking.estimateMaxCapacity(pulsar.getLocalZkCacheService().getResourceQuotaCache().getDefaultQuota());
long finalRank = 0;
if (strategy.equals(LOADBALANCER_STRATEGY_LLS)) {
finalRank = (long) loadPercentage;
} else if (strategy.equals(LOADBALANCER_STRATEGY_LEAST_MSG)) {
finalRank = (long) ranking.getEstimatedMessageRate();
} else {
double idleRatio = (100 - loadPercentage) / 100;
finalRank = (long) (maxCapacity * idleRatio * idleRatio);
}
if (!newSortedRankings.containsKey(finalRank)) {
newSortedRankings.put(finalRank, new HashSet<ResourceUnit>());
}
newSortedRankings.get(finalRank).add(entry.getKey());
if (log.isDebugEnabled()) {
log.debug("Added Resource Unit [{}] with Rank [{}]", entry.getKey().getResourceId(), finalRank);
}
// update metrics
if (resourceUnit.getResourceId().contains(hostname)) {
updateLoadBalancingMetrics(hostname, finalRank, ranking);
}
}
updateBrokerToNamespaceToBundle();
this.sortedRankings.set(newSortedRankings);
this.resourceUnitRankings = newResourceUnitRankings;
} else {
log.info("Leader broker[{}] No ResourceUnits to rank this run, Using Old Ranking", pulsar.getWebServiceAddress());
}
}
use of org.apache.pulsar.broker.loadbalance.ResourceUnit in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method doLoadShedding.
@Override
public void doLoadShedding() {
long overloadThreshold = this.getLoadBalancerBrokerOverloadedThresholdPercentage();
long comfortLoadLevel = this.getLoadBalancerBrokerComfortLoadThresholdPercentage();
log.info("Running load shedding task as leader broker, overload threshold {}, comfort loadlevel {}", overloadThreshold, comfortLoadLevel);
// overloadedRU --> bundleName
Map<ResourceUnit, String> namespaceBundlesToBeUnloaded = new HashMap<>();
synchronized (currentLoadReports) {
for (Map.Entry<ResourceUnit, LoadReport> entry : currentLoadReports.entrySet()) {
ResourceUnit overloadedRU = entry.getKey();
LoadReport lr = entry.getValue();
if (isAboveLoadLevel(lr.getSystemResourceUsage(), overloadThreshold)) {
ResourceType bottleneckResourceType = lr.getBottleneckResourceType();
Map<String, NamespaceBundleStats> bundleStats = lr.getSortedBundleStats(bottleneckResourceType);
if (bundleStats == null) {
log.warn("Null bundle stats for bundle {}", lr.getName());
continue;
}
// 1. owns only one namespace
if (bundleStats.size() == 1) {
// can't unload one namespace, just issue a warning message
String bundleName = lr.getBundleStats().keySet().iterator().next();
log.warn("HIGH USAGE WARNING : Sole namespace bundle {} is overloading broker {}. " + "No Load Shedding will be done on this broker", bundleName, overloadedRU.getResourceId());
continue;
}
for (Map.Entry<String, NamespaceBundleStats> bundleStat : bundleStats.entrySet()) {
String bundleName = bundleStat.getKey();
NamespaceBundleStats stats = bundleStat.getValue();
// We need at least one underloaded RU from list of candidates that can host this bundle
if (isBrokerAvailableForRebalancing(bundleStat.getKey(), comfortLoadLevel)) {
log.info("Namespace bundle {} will be unloaded from overloaded broker {}, bundle stats (topics: {}, producers {}, " + "consumers {}, bandwidthIn {}, bandwidthOut {})", bundleName, overloadedRU.getResourceId(), stats.topics, stats.producerCount, stats.consumerCount, stats.msgThroughputIn, stats.msgThroughputOut);
namespaceBundlesToBeUnloaded.put(overloadedRU, bundleName);
} else {
log.info("Unable to shed load from broker {}, no brokers with enough capacity available " + "for re-balancing {}", overloadedRU.getResourceId(), bundleName);
}
break;
}
}
}
}
unloadNamespacesFromOverLoadedBrokers(namespaceBundlesToBeUnloaded);
}
use of org.apache.pulsar.broker.loadbalance.ResourceUnit in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method getFinalCandidates.
private Multimap<Long, ResourceUnit> getFinalCandidates(ServiceUnitId serviceUnit, Map<Long, Set<ResourceUnit>> availableBrokers) {
synchronized (brokerCandidateCache) {
final Multimap<Long, ResourceUnit> result = TreeMultimap.create();
availableBrokersCache.clear();
for (final Set<ResourceUnit> resourceUnits : availableBrokers.values()) {
for (final ResourceUnit resourceUnit : resourceUnits) {
availableBrokersCache.add(resourceUnit.getResourceId().replace("http://", ""));
}
}
brokerCandidateCache.clear();
try {
LoadManagerShared.applyNamespacePolicies(serviceUnit, policies, brokerCandidateCache, availableBrokersCache, brokerTopicLoadingPredicate);
} catch (Exception e) {
log.warn("Error when trying to apply policies: {}", e);
for (final Map.Entry<Long, Set<ResourceUnit>> entry : availableBrokers.entrySet()) {
result.putAll(entry.getKey(), entry.getValue());
}
return result;
}
// As long as there is at least one broker left, this will always leave brokerCandidateCache non-empty.
LoadManagerShared.removeMostServicingBrokersForNamespace(serviceUnit.toString(), brokerCandidateCache, brokerToNamespaceToBundleRange);
// After LoadManagerShared is finished applying the filter, put the results back into a multimap.
for (final Map.Entry<Long, Set<ResourceUnit>> entry : availableBrokers.entrySet()) {
final Long rank = entry.getKey();
final Set<ResourceUnit> resourceUnits = entry.getValue();
for (final ResourceUnit resourceUnit : resourceUnits) {
if (brokerCandidateCache.contains(resourceUnit.getResourceId().replace("http://", ""))) {
result.put(rank, resourceUnit);
}
}
}
return result;
}
}
use of org.apache.pulsar.broker.loadbalance.ResourceUnit in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method isBrokerAvailableForRebalancing.
// todo: changeme: this can be optimized, we don't have to iterate through everytime
private boolean isBrokerAvailableForRebalancing(String bundleName, long maxLoadLevel) {
NamespaceName namespaceName = NamespaceName.get(LoadManagerShared.getNamespaceNameFromBundleName(bundleName));
Map<Long, Set<ResourceUnit>> availableBrokers = sortedRankings.get();
// this does not have "http://" in front, hacky but no time to pretty up
Multimap<Long, ResourceUnit> brokers = getFinalCandidates(namespaceName, availableBrokers);
for (Object broker : brokers.values()) {
ResourceUnit underloadedRU = (ResourceUnit) broker;
LoadReport currentLoadReport = currentLoadReports.get(underloadedRU);
if (isBelowLoadLevel(currentLoadReport.getSystemResourceUsage(), maxLoadLevel)) {
return true;
}
}
return false;
}
use of org.apache.pulsar.broker.loadbalance.ResourceUnit in project incubator-pulsar by apache.
the class WRRPlacementStrategy method findBrokerForPlacement.
/**
* <code>
* Function : getByWeightedRoundRobin
* returns ResourceUnit selected by WRR algorithm based on available resource on RU
* ^
* |
* |
* |
* | | | | |
* | | | | |
* | Broker 2 | Broker 3 | Broker 1 | B4 |
* | | | | |
* +----------------+------------------------+--------------------------------+--------->
* 0 20 50 90 100
*
* This is weighted Round robin, we calculate weight based on availability of resources;
* total availability is taken as a full range then each broker is given range based on
* its resource availability, if the number generated within total range happens to be in
* broker's range, that broker is selected
* </code>
*/
public ResourceUnit findBrokerForPlacement(Multimap<Long, ResourceUnit> finalCandidates) {
if (finalCandidates.isEmpty()) {
return null;
}
log.debug("Total Final Candidates selected - [{}]", finalCandidates.size());
int totalAvailability = 0;
for (Map.Entry<Long, ResourceUnit> candidateOwner : finalCandidates.entries()) {
totalAvailability += candidateOwner.getKey().intValue();
}
ResourceUnit selectedRU = null;
if (totalAvailability <= 0) {
// for now, pick anyone and return that one, because when we don't have ranking we put O for each broker
return Iterables.get(finalCandidates.get(0L), rand.nextInt(finalCandidates.size()));
}
int weightedSelector = rand.nextInt(totalAvailability);
log.debug("Generated Weighted Selector Number - [{}] ", weightedSelector);
int weightRangeSoFar = 0;
for (Map.Entry<Long, ResourceUnit> candidateOwner : finalCandidates.entries()) {
weightRangeSoFar += candidateOwner.getKey();
if (weightedSelector < weightRangeSoFar) {
selectedRU = candidateOwner.getValue();
log.debug(" Weighted Round Robin Selected RU - [{}]", candidateOwner.getValue().getResourceId());
break;
}
}
return selectedRU;
}
Aggregations