1818package org .apache .flink .autoscaler ;
1919
2020import org .apache .flink .annotation .VisibleForTesting ;
21+ import org .apache .flink .api .java .tuple .Tuple2 ;
2122import org .apache .flink .autoscaler .config .AutoScalerOptions ;
2223import org .apache .flink .autoscaler .metrics .CollectedMetricHistory ;
2324import org .apache .flink .autoscaler .metrics .CollectedMetrics ;
25+ import org .apache .flink .autoscaler .metrics .CustomEvaluator ;
2426import org .apache .flink .autoscaler .metrics .EvaluatedMetrics ;
2527import org .apache .flink .autoscaler .metrics .EvaluatedScalingMetric ;
2628import org .apache .flink .autoscaler .metrics .MetricAggregator ;
2729import org .apache .flink .autoscaler .metrics .ScalingMetric ;
2830import org .apache .flink .autoscaler .topology .JobTopology ;
2931import org .apache .flink .autoscaler .utils .AutoScalerUtils ;
3032import org .apache .flink .configuration .Configuration ;
33+ import org .apache .flink .configuration .UnmodifiableConfiguration ;
3134import org .apache .flink .runtime .jobgraph .JobVertexID ;
3235
3336import org .slf4j .Logger ;
3841
3942import java .time .Duration ;
4043import java .time .Instant ;
44+ import java .util .Collections ;
4145import java .util .HashMap ;
4246import java .util .Map ;
4347import java .util .Optional ;
@@ -72,14 +76,34 @@ public class ScalingMetricEvaluator {
7276 private static final Logger LOG = LoggerFactory .getLogger (ScalingMetricEvaluator .class );
7377
7478 public EvaluatedMetrics evaluate (
75- Configuration conf , CollectedMetricHistory collectedMetrics , Duration restartTime ) {
79+ Configuration conf ,
80+ CollectedMetricHistory collectedMetrics ,
81+ Duration restartTime ,
82+ @ Nullable Tuple2 <CustomEvaluator , Configuration > customEvaluatorWithConfig ) {
7683 LOG .debug ("Restart time used in metrics evaluation: {}" , restartTime );
7784 var scalingOutput = new HashMap <JobVertexID , Map <ScalingMetric , EvaluatedScalingMetric >>();
7885 var metricsHistory = collectedMetrics .getMetricHistory ();
7986 var topology = collectedMetrics .getJobTopology ();
8087
8188 boolean processingBacklog = isProcessingBacklog (topology , metricsHistory , conf );
8289
90+ var customEvaluationSession =
91+ Optional .ofNullable (customEvaluatorWithConfig )
92+ .map (
93+ info ->
94+ Tuple2 .of (
95+ info .f0 ,
96+ new CustomEvaluator .Context (
97+ new UnmodifiableConfiguration (conf ),
98+ Collections .unmodifiableSortedMap (
99+ metricsHistory ),
100+ Collections .unmodifiableMap (scalingOutput ),
101+ topology ,
102+ processingBacklog ,
103+ restartTime ,
104+ info .f1 )))
105+ .orElse (null );
106+
83107 for (var vertex : topology .getVerticesInTopologicalOrder ()) {
84108 scalingOutput .put (
85109 vertex ,
@@ -90,7 +114,8 @@ public EvaluatedMetrics evaluate(
90114 topology ,
91115 vertex ,
92116 processingBacklog ,
93- restartTime ));
117+ restartTime ,
118+ customEvaluationSession ));
94119 }
95120
96121 var globalMetrics = evaluateGlobalMetrics (metricsHistory );
@@ -132,7 +157,8 @@ private Map<ScalingMetric, EvaluatedScalingMetric> evaluateMetrics(
132157 JobTopology topology ,
133158 JobVertexID vertex ,
134159 boolean processingBacklog ,
135- Duration restartTime ) {
160+ Duration restartTime ,
161+ @ Nullable Tuple2 <CustomEvaluator , CustomEvaluator .Context > customEvaluationSession ) {
136162
137163 var latestVertexMetrics =
138164 metricsHistory .get (metricsHistory .lastKey ()).getVertexMetrics ().get (vertex );
@@ -142,6 +168,7 @@ private Map<ScalingMetric, EvaluatedScalingMetric> evaluateMetrics(
142168 double inputRateAvg = getRate (ScalingMetric .NUM_RECORDS_IN , vertex , metricsHistory );
143169
144170 var evaluatedMetrics = new HashMap <ScalingMetric , EvaluatedScalingMetric >();
171+
145172 computeTargetDataRate (
146173 topology ,
147174 vertex ,
@@ -175,6 +202,24 @@ private Map<ScalingMetric, EvaluatedScalingMetric> evaluateMetrics(
175202 EvaluatedScalingMetric .of (vertexInfo .getNumSourcePartitions ()));
176203
177204 computeProcessingRateThresholds (evaluatedMetrics , conf , processingBacklog , restartTime );
205+
206+ Optional .ofNullable (customEvaluationSession )
207+ .map (
208+ session ->
209+ runCustomEvaluator (
210+ vertex ,
211+ Collections .unmodifiableMap (evaluatedMetrics ),
212+ session ))
213+ .filter (customEvaluatedMetrics -> !customEvaluatedMetrics .isEmpty ())
214+ .ifPresent (
215+ customEvaluatedMetrics -> {
216+ LOG .info (
217+ "Merging custom evaluated metrics for vertex {}: {}" ,
218+ vertex ,
219+ customEvaluatedMetrics );
220+ mergeEvaluatedMetricsMaps (evaluatedMetrics , customEvaluatedMetrics );
221+ });
222+
178223 return evaluatedMetrics ;
179224 }
180225
@@ -585,4 +630,51 @@ protected static double computeEdgeDataRate(
585630 to );
586631 return getRate (ScalingMetric .NUM_RECORDS_OUT , from , metricsHistory );
587632 }
633+
634+ @ VisibleForTesting
635+ protected static Map <ScalingMetric , EvaluatedScalingMetric > runCustomEvaluator (
636+ JobVertexID vertex ,
637+ Map <ScalingMetric , EvaluatedScalingMetric > evaluatedMetrics ,
638+ Tuple2 <CustomEvaluator , CustomEvaluator .Context > customEvaluationSession ) {
639+ try {
640+ return customEvaluationSession .f0 .evaluateVertexMetrics (
641+ vertex , evaluatedMetrics , customEvaluationSession .f1 );
642+ } catch (UnsupportedOperationException e ) {
643+ LOG .warn (
644+ "Custom evaluator {} tried accessing an un-modifiable view." ,
645+ customEvaluationSession .f0 .getClass (),
646+ e );
647+ } catch (Exception e ) {
648+ LOG .warn (
649+ "Custom evaluator {} threw an exception." ,
650+ customEvaluationSession .f0 .getClass (),
651+ e );
652+ }
653+
654+ return Collections .emptyMap ();
655+ }
656+
657+ @ VisibleForTesting
658+ protected static void mergeEvaluatedMetricsMaps (
659+ Map <ScalingMetric , EvaluatedScalingMetric > actual ,
660+ @ Nullable Map <ScalingMetric , EvaluatedScalingMetric > incoming ) {
661+ Optional .ofNullable (incoming )
662+ .ifPresent (
663+ customEvaluatedMetric ->
664+ customEvaluatedMetric .forEach (
665+ (scalingMetric , evaluatedScalingMetric ) ->
666+ actual .merge (
667+ scalingMetric ,
668+ evaluatedScalingMetric ,
669+ ScalingMetricEvaluator
670+ ::mergeEvaluatedScalingMetric )));
671+ }
672+
673+ @ VisibleForTesting
674+ protected static EvaluatedScalingMetric mergeEvaluatedScalingMetric (
675+ EvaluatedScalingMetric actual , EvaluatedScalingMetric incoming ) {
676+ return new EvaluatedScalingMetric (
677+ !Double .isNaN (incoming .getCurrent ()) ? incoming .getCurrent () : actual .getCurrent (),
678+ !Double .isNaN (incoming .getAverage ()) ? incoming .getAverage () : actual .getAverage ());
679+ }
588680}
0 commit comments