33
33
import org .apache .flink .configuration .Configuration ;
34
34
import org .apache .flink .configuration .MemorySize ;
35
35
import org .apache .flink .configuration .TaskManagerOptions ;
36
+ import org .apache .flink .runtime .instance .SlotSharingGroupId ;
36
37
import org .apache .flink .runtime .jobgraph .JobVertexID ;
37
38
38
39
import org .slf4j .Logger ;
@@ -67,6 +68,9 @@ public class ScalingExecutor<KEY, Context extends JobAutoScalerContext<KEY>> {
67
68
public static final String HEAP_USAGE_MESSAGE =
68
69
"Heap Usage %s is above the allowed limit for scaling operations. Please adjust the available memory manually." ;
69
70
71
+ public static final String RESOURCE_QUOTA_REACHED_MESSAGE =
72
+ "Resource usage is above the allowed limit for scaling operations. Please adjust the resource quota manually." ;
73
+
70
74
private static final Logger LOG = LoggerFactory .getLogger (ScalingExecutor .class );
71
75
72
76
private final JobVertexScaler <KEY , Context > jobVertexScaler ;
@@ -129,8 +133,10 @@ public boolean scaleResource(
129
133
scalingSummaries ,
130
134
autoScalerEventHandler );
131
135
132
- if (scalingWouldExceedClusterResources (
133
- configOverrides .newConfigWithOverrides (conf ),
136
+ var memoryTuningEnabled = conf .get (AutoScalerOptions .MEMORY_TUNING_ENABLED );
137
+ if (scalingWouldExceedMaxResources (
138
+ memoryTuningEnabled ? configOverrides .newConfigWithOverrides (conf ) : conf ,
139
+ jobTopology ,
134
140
evaluatedMetrics ,
135
141
scalingSummaries ,
136
142
context )) {
@@ -280,6 +286,30 @@ private boolean isJobUnderMemoryPressure(
280
286
return false ;
281
287
}
282
288
289
+ @ VisibleForTesting
290
+ protected boolean scalingWouldExceedMaxResources (
291
+ Configuration tunedConfig ,
292
+ JobTopology jobTopology ,
293
+ EvaluatedMetrics evaluatedMetrics ,
294
+ Map <JobVertexID , ScalingSummary > scalingSummaries ,
295
+ Context ctx ) {
296
+ if (scalingWouldExceedClusterResources (
297
+ tunedConfig , evaluatedMetrics , scalingSummaries , ctx )) {
298
+ return true ;
299
+ }
300
+ if (scalingWouldExceedResourceQuota (tunedConfig , jobTopology , scalingSummaries , ctx )) {
301
+ autoScalerEventHandler .handleEvent (
302
+ ctx ,
303
+ AutoScalerEventHandler .Type .Warning ,
304
+ "ResourceQuotaReached" ,
305
+ RESOURCE_QUOTA_REACHED_MESSAGE ,
306
+ null ,
307
+ tunedConfig .get (SCALING_EVENT_INTERVAL ));
308
+ return true ;
309
+ }
310
+ return false ;
311
+ }
312
+
283
313
private boolean scalingWouldExceedClusterResources (
284
314
Configuration tunedConfig ,
285
315
EvaluatedMetrics evaluatedMetrics ,
@@ -306,7 +336,7 @@ private boolean scalingWouldExceedClusterResources(
306
336
ResourceCheckUtils .estimateNumTaskSlotsAfterRescale (
307
337
evaluatedMetrics .getVertexMetrics (), scalingSummaries , numTaskSlotsUsed );
308
338
309
- int taskSlotsPerTm = ctx . getConfiguration () .get (TaskManagerOptions .NUM_TASK_SLOTS );
339
+ int taskSlotsPerTm = tunedConfig .get (TaskManagerOptions .NUM_TASK_SLOTS );
310
340
311
341
int currentNumTms = (int ) Math .ceil (numTaskSlotsUsed / (double ) taskSlotsPerTm );
312
342
int newNumTms = (int ) Math .ceil (numTaskSlotsAfterRescale / (double ) taskSlotsPerTm );
@@ -315,6 +345,83 @@ private boolean scalingWouldExceedClusterResources(
315
345
currentNumTms , newNumTms , taskManagerCpu , taskManagerMemory );
316
346
}
317
347
348
+ protected static boolean scalingWouldExceedResourceQuota (
349
+ Configuration tunedConfig ,
350
+ JobTopology jobTopology ,
351
+ Map <JobVertexID , ScalingSummary > scalingSummaries ,
352
+ JobAutoScalerContext <?> ctx ) {
353
+
354
+ if (jobTopology == null || jobTopology .getSlotSharingGroupMapping ().isEmpty ()) {
355
+ return false ;
356
+ }
357
+
358
+ var cpuQuota = tunedConfig .getOptional (AutoScalerOptions .CPU_QUOTA );
359
+ var memoryQuota = tunedConfig .getOptional (AutoScalerOptions .MEMORY_QUOTA );
360
+ var tmMemory = MemoryTuning .getTotalMemory (tunedConfig , ctx );
361
+ var tmCpu = ctx .getTaskManagerCpu ().orElse (0. );
362
+
363
+ if (cpuQuota .isPresent () || memoryQuota .isPresent ()) {
364
+ var currentSlotSharingGroupMaxParallelisms = new HashMap <SlotSharingGroupId , Integer >();
365
+ var newSlotSharingGroupMaxParallelisms = new HashMap <SlotSharingGroupId , Integer >();
366
+ for (var e : jobTopology .getSlotSharingGroupMapping ().entrySet ()) {
367
+ int currentMaxParallelism =
368
+ e .getValue ().stream ()
369
+ .filter (scalingSummaries ::containsKey )
370
+ .mapToInt (v -> scalingSummaries .get (v ).getCurrentParallelism ())
371
+ .max ()
372
+ .orElse (0 );
373
+ currentSlotSharingGroupMaxParallelisms .put (e .getKey (), currentMaxParallelism );
374
+ int newMaxParallelism =
375
+ e .getValue ().stream ()
376
+ .filter (scalingSummaries ::containsKey )
377
+ .mapToInt (v -> scalingSummaries .get (v ).getNewParallelism ())
378
+ .max ()
379
+ .orElse (0 );
380
+ newSlotSharingGroupMaxParallelisms .put (e .getKey (), newMaxParallelism );
381
+ }
382
+
383
+ var numSlotsPerTm = tunedConfig .get (TaskManagerOptions .NUM_TASK_SLOTS );
384
+ var currentTotalSlots =
385
+ currentSlotSharingGroupMaxParallelisms .values ().stream ()
386
+ .mapToInt (Integer ::intValue )
387
+ .sum ();
388
+ var currentNumTms = currentTotalSlots / numSlotsPerTm ;
389
+ var newTotalSlots =
390
+ newSlotSharingGroupMaxParallelisms .values ().stream ()
391
+ .mapToInt (Integer ::intValue )
392
+ .sum ();
393
+ var newNumTms = newTotalSlots / numSlotsPerTm ;
394
+
395
+ if (newNumTms <= currentNumTms ) {
396
+ LOG .debug (
397
+ "Skipping quota check due to new resource allocation is less or equals than the current" );
398
+ return false ;
399
+ }
400
+
401
+ if (cpuQuota .isPresent ()) {
402
+ LOG .debug ("CPU resource quota is {}, checking limits" , cpuQuota .get ());
403
+ double totalCPU = tmCpu * newNumTms ;
404
+ if (totalCPU > cpuQuota .get ()) {
405
+ LOG .info ("CPU resource quota reached with value: {}" , totalCPU );
406
+ return true ;
407
+ }
408
+ }
409
+
410
+ if (memoryQuota .isPresent ()) {
411
+ LOG .debug ("Memory resource quota is {}, checking limits" , memoryQuota .get ());
412
+ long totalMemory = tmMemory .getBytes () * newNumTms ;
413
+ if (totalMemory > memoryQuota .get ().getBytes ()) {
414
+ LOG .info (
415
+ "Memory resource quota reached with value: {}" ,
416
+ new MemorySize (totalMemory ));
417
+ return true ;
418
+ }
419
+ }
420
+ }
421
+
422
+ return false ;
423
+ }
424
+
318
425
private static Map <String , String > getVertexParallelismOverrides (
319
426
Map <JobVertexID , Map <ScalingMetric , EvaluatedScalingMetric >> evaluatedMetrics ,
320
427
Map <JobVertexID , ScalingSummary > summaries ) {
0 commit comments