-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[feat][broker] Support lower boundary shedding for ThresholdShedder #17456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fa33461
a716306
336454c
12396a4
8843bd4
712d7f4
6cf5c98
b3febe9
19b553a
2371def
2ecddc1
b9bc2e2
1f5d4e4
1ecb7d6
633a27c
d3ce511
257e998
cb2403d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,9 @@ public class ThresholdShedder implements LoadSheddingStrategy { | |
| private static final Logger log = LoggerFactory.getLogger(ThresholdShedder.class); | ||
| private final Multimap<String, String> selectedBundlesCache = ArrayListMultimap.create(); | ||
| private static final double ADDITIONAL_THRESHOLD_PERCENT_MARGIN = 0.05; | ||
|
|
||
| private static final double LOWER_BOUNDARY_THRESHOLD_MARGIN = 0.5; | ||
|
|
||
| private static final double MB = 1024 * 1024; | ||
|
|
||
| private static final long LOAD_LOG_SAMPLE_DELAY_IN_SEC = 5 * 60; // 5 mins | ||
|
|
@@ -80,8 +83,7 @@ public Multimap<String, String> findBundlesForUnloading(final LoadData loadData, | |
| final Map<String, Long> recentlyUnloadedBundles = loadData.getRecentlyUnloadedBundles(); | ||
| final double minThroughputThreshold = conf.getLoadBalancerBundleUnloadMinThroughputThreshold() * MB; | ||
|
|
||
| final double avgUsage = getBrokerAvgUsage( | ||
| loadData, conf.getLoadBalancerHistoryResourcePercentage(), conf, sampleLog); | ||
| final double avgUsage = getBrokerAvgUsage(loadData, conf, sampleLog); | ||
| if (sampleLog) { | ||
| log.info("brokers' resource avgUsage:{}%", toPercentage(avgUsage)); | ||
| } | ||
|
|
@@ -122,17 +124,34 @@ public Multimap<String, String> findBundlesForUnloading(final LoadData loadData, | |
| broker, 100 * currentUsage, 100 * avgUsage, 100 * threshold, minimumThroughputToOffload / MB, | ||
| (brokerCurrentThroughput - minimumThroughputToOffload) / MB); | ||
|
|
||
| MutableDouble trafficMarkedToOffload = new MutableDouble(0); | ||
| MutableBoolean atLeastOneBundleSelected = new MutableBoolean(false); | ||
|
|
||
| if (localData.getBundles().size() > 1) { | ||
| loadData.getBundleDataForLoadShedding().entrySet().stream() | ||
| .map((e) -> { | ||
| String bundle = e.getKey(); | ||
| BundleData bundleData = e.getValue(); | ||
| TimeAverageMessageData shortTermData = bundleData.getShortTermData(); | ||
| double throughput = shortTermData.getMsgThroughputIn() + shortTermData.getMsgThroughputOut(); | ||
| return Pair.of(bundle, throughput); | ||
| filterAndSelectBundle(loadData, recentlyUnloadedBundles, broker, localData, minimumThroughputToOffload); | ||
| } else if (localData.getBundles().size() == 1) { | ||
| log.warn( | ||
| "HIGH USAGE WARNING : Sole namespace bundle {} is overloading broker {}. " | ||
| + "No Load Shedding will be done on this broker", | ||
| localData.getBundles().iterator().next(), broker); | ||
| } else { | ||
| log.warn("Broker {} is overloaded despite having no bundles", broker); | ||
| } | ||
| }); | ||
| if (selectedBundlesCache.isEmpty() && conf.isLowerBoundarySheddingEnabled()) { | ||
| tryLowerBoundaryShedding(loadData, conf); | ||
| } | ||
| return selectedBundlesCache; | ||
| } | ||
|
|
||
| private void filterAndSelectBundle(LoadData loadData, Map<String, Long> recentlyUnloadedBundles, String broker, | ||
| LocalBrokerData localData, double minimumThroughputToOffload) { | ||
| MutableDouble trafficMarkedToOffload = new MutableDouble(0); | ||
| MutableBoolean atLeastOneBundleSelected = new MutableBoolean(false); | ||
| loadData.getBundleDataForLoadShedding().entrySet().stream() | ||
| .map((e) -> { | ||
| String bundle = e.getKey(); | ||
| BundleData bundleData = e.getValue(); | ||
| TimeAverageMessageData shortTermData = bundleData.getShortTermData(); | ||
| double throughput = shortTermData.getMsgThroughputIn() + shortTermData.getMsgThroughputOut(); | ||
| return Pair.of(bundle, throughput); | ||
| }).filter(e -> | ||
| !recentlyUnloadedBundles.containsKey(e.getLeft()) | ||
| ).filter(e -> | ||
|
|
@@ -147,21 +166,11 @@ public Multimap<String, String> findBundlesForUnloading(final LoadData loadData, | |
| atLeastOneBundleSelected.setTrue(); | ||
| } | ||
| }); | ||
| } else if (localData.getBundles().size() == 1) { | ||
| log.warn( | ||
| "HIGH USAGE WARNING : Sole namespace bundle {} is overloading broker {}. " | ||
| + "No Load Shedding will be done on this broker", | ||
| localData.getBundles().iterator().next(), broker); | ||
| } else { | ||
| log.warn("Broker {} is overloaded despite having no bundles", broker); | ||
| } | ||
| }); | ||
|
|
||
| return selectedBundlesCache; | ||
| } | ||
|
|
||
| private double getBrokerAvgUsage(final LoadData loadData, final double historyPercentage, | ||
| private double getBrokerAvgUsage(final LoadData loadData, | ||
| final ServiceConfiguration conf, boolean sampleLog) { | ||
| double historyPercentage = conf.getLoadBalancerHistoryResourcePercentage(); | ||
| double totalUsage = 0.0; | ||
| int totalBrokers = 0; | ||
|
|
||
|
|
@@ -227,4 +236,60 @@ private double updateAvgResourceUsage(String broker, LocalBrokerData localBroker | |
| return historyUsage; | ||
| } | ||
|
|
||
| private void tryLowerBoundaryShedding(LoadData loadData, ServiceConfiguration conf) { | ||
| // Select the broker with the most resource usage. | ||
| final double threshold = conf.getLoadBalancerBrokerThresholdShedderPercentage() / 100.0; | ||
| final double avgUsage = getBrokerAvgUsage(loadData, conf, canSampleLog()); | ||
| Pair<Boolean, String> result = getMaxUsageBroker(loadData, threshold, avgUsage); | ||
| boolean hasBrokerBelowLowerBound = result.getLeft(); | ||
| String maxUsageBroker = result.getRight(); | ||
| BrokerData brokerData = loadData.getBrokerData().get(maxUsageBroker); | ||
| if (brokerData == null) { | ||
| log.info("Load data is null or bundle <=1, skipping bundle unload."); | ||
| return; | ||
| } | ||
| if (!hasBrokerBelowLowerBound) { | ||
| log.info("No broker is below the lower bound, threshold is {}, " | ||
| + "avgUsage usage is {}, max usage of Broker {} is {}", | ||
| threshold, avgUsage, maxUsageBroker, | ||
| brokerAvgResourceUsage.getOrDefault(maxUsageBroker, 0.0)); | ||
| return; | ||
| } | ||
| LocalBrokerData localData = brokerData.getLocalData(); | ||
|
315157973 marked this conversation as resolved.
|
||
| double brokerCurrentThroughput = localData.getMsgThroughputIn() + localData.getMsgThroughputOut(); | ||
| double minimumThroughputToOffload = brokerCurrentThroughput * threshold * LOWER_BOUNDARY_THRESHOLD_MARGIN; | ||
|
315157973 marked this conversation as resolved.
|
||
| double minThroughputThreshold = conf.getLoadBalancerBundleUnloadMinThroughputThreshold() * MB; | ||
| if (minThroughputThreshold > minimumThroughputToOffload) { | ||
| log.info("broker {} in lower boundary shedding is planning to shed throughput {} MByte/s less than " | ||
| + "minimumThroughputThreshold {} MByte/s, skipping bundle unload.", | ||
| maxUsageBroker, minimumThroughputToOffload / MB, minThroughputThreshold / MB); | ||
| return; | ||
| } | ||
| filterAndSelectBundle(loadData, loadData.getRecentlyUnloadedBundles(), maxUsageBroker, localData, | ||
| minimumThroughputToOffload); | ||
| } | ||
|
|
||
| private Pair<Boolean, String> getMaxUsageBroker( | ||
| LoadData loadData, double threshold, double avgUsage) { | ||
| String maxUsageBrokerName = ""; | ||
| double maxUsage = avgUsage - threshold; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The max usage broker load: > (avgUsage - threshold) After the max usage broker unloads to the lower broker, the max usage broker might become the lower broker, the lower broker becomes the max usage broker. Can this will lead to frequent bundle unloading? It looks like we need to change double minimumThroughputToOffload = brokerCurrentThroughput * threshold * LOWER_BOUNDARY_THRESHOLD_MARGIN;to double minimumThroughputToOffload = Math.min(brokerCurrentThroughput * threshold * LOWER_BOUNDARY_THRESHOLD_MARGIN, brokerCurrentThroughput - avgUsage - threshold);
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And we should also add a test for this case.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does not solve the problem. The best way to solve this problem is to split bundle. |
||
| boolean hasBrokerBelowLowerBound = false; | ||
| for (Map.Entry<String, BrokerData> entry : loadData.getBrokerData().entrySet()) { | ||
| String broker = entry.getKey(); | ||
| BrokerData brokerData = entry.getValue(); | ||
| double currentUsage = brokerAvgResourceUsage.getOrDefault(broker, 0.0); | ||
| // Select the broker with the most resource usage. | ||
| if (currentUsage > maxUsage && brokerData.getLocalData() != null | ||
| && brokerData.getLocalData().getBundles().size() > 1) { | ||
| maxUsage = currentUsage; | ||
| maxUsageBrokerName = broker; | ||
|
315157973 marked this conversation as resolved.
|
||
| } | ||
| // Whether any brokers with low usage in the cluster. | ||
| if (currentUsage < avgUsage - threshold) { | ||
| hasBrokerBelowLowerBound = true; | ||
|
315157973 marked this conversation as resolved.
|
||
| } | ||
| } | ||
| return Pair.of(hasBrokerBelowLowerBound, maxUsageBrokerName); | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.