-
Notifications
You must be signed in to change notification settings - Fork 3.8k
feat: Introduce lag emergency option for cost-based autoscaler #19655
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
base: master
Are you sure you want to change the base?
Changes from all commits
f402975
d555233
ffa2092
dca6cf7
ad6e74a
9c68e50
e37f45f
0062328
a9c1c1d
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 |
|---|---|---|
|
|
@@ -214,6 +214,25 @@ public CostBasedAutoScalerConfig getConfig() | |
| return config; | ||
| } | ||
|
|
||
| private boolean isCriticalLag(CostMetrics metrics) | ||
| { | ||
| final Long criticalLagThreshold = config.getCriticalLagThreshold(); | ||
| return metrics != null && criticalLagThreshold != null | ||
| && metrics.getAggregateLag() >= criticalLagThreshold * WeightedCostFunction.CRITICAL_LAG_TIER1_FRACTION; | ||
|
Fly-Style marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /** | ||
| * Whether the last collected metrics crossed {@link WeightedCostFunction#CRITICAL_LAG_TIER2_FRACTION} of | ||
| * {@link CostBasedAutoScalerConfig#getCriticalLagThreshold()}, meaning the argmin search should be | ||
| * skipped entirely in favor of jumping straight to the maximum task count. | ||
| */ | ||
| private boolean isEmergencyLag(CostMetrics metrics) | ||
| { | ||
| final Long criticalLagThreshold = config.getCriticalLagThreshold(); | ||
| return metrics != null && criticalLagThreshold != null | ||
| && metrics.getAggregateLag() >= criticalLagThreshold * WeightedCostFunction.CRITICAL_LAG_TIER2_FRACTION; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the lowest-cost task count given {@code metrics}, or {@link #CANNOT_COMPUTE} when | ||
| * metrics are unusable. Returning the current task count means the current count is already | ||
|
|
@@ -250,6 +269,24 @@ int computeOptimalTaskCount(CostMetrics metrics) | |
| return currentTaskCount; | ||
| } | ||
|
|
||
| final boolean criticalLag = isCriticalLag(metrics); | ||
| final boolean emergencyLag = isEmergencyLag(metrics); | ||
| if (emergencyLag) { | ||
| log.info( | ||
| "Supervisor[%s] aggregateLag[%.0f] crossed [%.0f%%] of criticalLagThreshold[%d]: skipping the argmin" | ||
| + " search and jumping straight to the maximum task count.", | ||
| supervisorId, metrics.getAggregateLag(), WeightedCostFunction.CRITICAL_LAG_TIER2_FRACTION * 100, | ||
| config.getCriticalLagThreshold() | ||
| ); | ||
| } else if (criticalLag) { | ||
| log.info( | ||
| "Supervisor[%s] aggregateLag[%.0f] crossed [%.0f%%] of criticalLagThreshold[%d]: widening scale-up" | ||
| + " candidates and maxing out the lag-amplification multiplier.", | ||
| supervisorId, metrics.getAggregateLag(), WeightedCostFunction.CRITICAL_LAG_TIER1_FRACTION * 100, | ||
| config.getCriticalLagThreshold() | ||
| ); | ||
| } | ||
|
|
||
| // Start with the current task count as optimal | ||
| final CostResult currentCost = costFunction.computeCost(metrics, currentTaskCount, config); | ||
| int optimalTaskCount = currentTaskCount; | ||
|
|
@@ -278,7 +315,7 @@ int computeOptimalTaskCount(CostMetrics metrics) | |
| int startIndex = 0; | ||
| int endIndex = validTaskCounts.length - 1; | ||
|
|
||
| if (config.isUseTaskCountBoundariesOnScaleUp()) { | ||
| if (config.isUseTaskCountBoundariesOnScaleUp() && !criticalLag) { | ||
| int currentTaskCountIndex = Arrays.binarySearch(validTaskCounts, currentTaskCount); | ||
| endIndex = currentTaskCountIndex >= 0 | ||
| ? Math.min(currentTaskCountIndex + BOUNDARY_LIMIT_IN_PARTITIONS_PER_TASK, endIndex) | ||
|
|
@@ -292,6 +329,12 @@ int computeOptimalTaskCount(CostMetrics metrics) | |
| : startIndex; | ||
| } | ||
|
|
||
| // Emergency (tier 2) lag skips the argmin search entirely: evaluate only the maximum valid task count. | ||
| if (emergencyLag) { | ||
|
Member
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. [P1] Bypass the cost-drop gate during tier-2 emergencies After this emergency branch restricts evaluation to the maximum task count, execution still reaches the existing |
||
| startIndex = validTaskCounts.length - 1; | ||
|
Member
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. [P1] Make emergency lag choose the max unconditionally The emergency path narrows the loop to only the maximum valid task count, but |
||
| endIndex = validTaskCounts.length - 1; | ||
| } | ||
|
|
||
| for (int i = startIndex; i <= endIndex; ++i) { | ||
| final int taskCount = validTaskCounts[i]; | ||
| CostResult costResult = costFunction.computeCost(metrics, taskCount, config); | ||
|
|
@@ -477,11 +520,14 @@ CostMetrics collectMetrics() | |
|
|
||
| final LagStats lagStats = supervisor.computeLagStats(); | ||
| final double avgPartitionLag; | ||
| final double aggregateLag; | ||
| if (lagStats == null) { | ||
| log.debug("Lag stats unavailable for supervisorId [%s], skipping collection", supervisorId); | ||
| avgPartitionLag = -1; | ||
| aggregateLag = -1; | ||
| } else { | ||
| avgPartitionLag = lagStats.getAvgLag(); | ||
| aggregateLag = lagStats.getTotalLag(); | ||
| } | ||
|
|
||
| final int currentTaskCount = supervisor.getIoConfig().getTaskCount(); | ||
|
|
@@ -497,6 +543,7 @@ CostMetrics collectMetrics() | |
|
|
||
| return new CostMetrics( | ||
| avgPartitionLag, | ||
| aggregateLag, | ||
| currentTaskCount, | ||
| partitionCount, | ||
| pollIdleRatio, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.