Dennis-Mircea commented on PR #1099:
URL: 
https://github.com/apache/flink-kubernetes-operator/pull/1099#issuecomment-4295606059

   **Question & Follow-up: single-evaluator limitation vs. config namespace 
shape**
   
   As it stands, `JobAutoScalerImpl#getCustomEvaluatorIfRequired` reads a 
single `ConfigOption<String> CUSTOM_EVALUATOR_NAME` 
(`job.autoscaler.metrics.custom-evaluator.name`), looks up one 
`FlinkAutoscalerEvaluator` from the registry (keyed by 
`FlinkAutoscalerEvaluator#getName()`), and `ScalingMetricEvaluator` only knows 
how to invoke one evaluator per evaluation cycle. So at runtime **only one 
custom evaluator can be active per pipeline**, even though several may be 
registered via `META-INF/services`.
   
   At the same time, `AutoScalerOptions.forCustomEvaluator(conf, name)` builds 
the evaluator-specific view as a `DelegatingConfiguration` over 
`job.autoscaler.metrics.custom-evaluator.<name>.`, which means users have to 
write keys like:
   
   ```yaml
   job.autoscaler.metrics.custom-evaluator.name: custom-evaluator
   job.autoscaler.metrics.custom-evaluator.custom-evaluator.target-data-rate: 
100000.0
   ```
   
   That repeated `<name>` segment is future-proofing for a multi-evaluator 
world, but today it's pure ceremony: there is never more than one evaluator 
reading from that namespace. It's confusing to document, easy to mis-type, and 
likely to trip users up.
   
   I see two coherent directions and would like the maintainers' preference 
before pushing further commits:
   
   **Option A - keep the single-evaluator contract, simplify the namespace.**
   
   Drop the `<name>` segment from `forCustomEvaluator` so 
`Context#getCustomEvaluatorConf()` is a delegating view over 
`job.autoscaler.metrics.custom-evaluator.` directly. To avoid the `name` key 
leaking into that view the selector would move out of that prefix (e.g. renamed 
to `job.autoscaler.metrics.custom-evaluator-name`, with a fallback key for the 
old one). User-facing config becomes:
   
   ```yaml
   job.autoscaler.metrics.custom-evaluator-name: custom-evaluator
   job.autoscaler.metrics.custom-evaluator.target-data-rate: 100000.0
   ```
   
   The **Option A** has the following pro & cons:
   - Pros: cleanest UX, matches the actual runtime semantics.
   - Cons: represents a big limitation in the custom evaluator flexibility.
   
   **Option B - lift the limitation, align the config shape with Flink metric 
reporters and with 
[FLIP-514](https://cwiki.apache.org/confluence/display/FLINK/FLIP-514).**
   
   Flink's metric-reporter config is the established idiom for "list of named 
instances, each with its own class and its own bag of options":
   
   ```properties
   metrics.reporters: my_jmx_reporter,my_other_reporter
   
   metrics.reporter.my_jmx_reporter.factory.class: 
org.apache.flink.metrics.jmx.JMXReporterFactory
   metrics.reporter.my_jmx_reporter.port: 9020-9040
   
   metrics.reporter.my_other_reporter.factory.class: 
org.apache.flink.metrics.graphite.GraphiteReporterFactory
   metrics.reporter.my_other_reporter.host: 192.168.1.1
   metrics.reporter.my_other_reporter.port: 10000
   ```
   
   Applied to custom evaluators, the mapping is:
   
   | Reporter concept                            | Evaluator equivalent         
                                                               |
   | ------------------------------------------- | 
-------------------------------------------------------------------------------------------
 |
   | `metrics.reporters: r1,r2`                  | 
`job.autoscaler.metrics.custom-evaluators: 
custom-evaluator-1,custom-evaluator-2`           |
   | `metrics.reporter.<r>.factory.class: FQN`   | 
`job.autoscaler.metrics.custom-evaluator.<instance>.class: FQN`                 
            |
   | `metrics.reporter.<r>.<opt>: v`             | 
`job.autoscaler.metrics.custom-evaluator.<instance>.<opt>: v`                   
            |
   
   Concretely:
   
   ```yaml
   job.autoscaler.metrics.custom-evaluators: 
custom-evaluator-1,custom-evaluator-2
   
   job.autoscaler.metrics.custom-evaluator.custom-evaluator-1.class: 
org.apache.flink.autoscaler.custom.MyFirstEvaluator
   job.autoscaler.metrics.custom-evaluator.custom-evaluator-1.target-data-rate: 
100000.0
   
   job.autoscaler.metrics.custom-evaluator.custom-evaluator-2.class: 
org.apache.flink.autoscaler.custom.MySecondEvaluator
   job.autoscaler.metrics.custom-evaluator.custom-evaluator-2.some-threshold: 
0.8
   ```
   
   The consequence is that the `getName()` goes away from the SPI. The instance 
name (`custom-evaluator-1`) is purely a configuration-level handle. Evaluator 
resolution stops going through the `META-INF/services` -> `getName()` registry 
and instead becomes: for each entry in `custom-evaluators`, read its 
`...<instance>.class` property and instantiate the fully qualified name (via 
`Class#forName`, or via a factory SPI if we want DI/customization, analogous to 
`*ReporterFactory`). This is exactly the shape 
[FLIP-514](https://cwiki.apache.org/confluence/display/FLINK/FLIP-514) proposes 
in its [Job Level Config For Custom 
Evaluator](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=345377351#FLIP514:CustomEvaluatorpluginforFlinkAutoscaler-JobLevelConfigForCustomEvaluator)
 section, and it removes the oddity where two evaluators couldn't share the 
same `getName()` even though they were distinct implementations.
   
   To keep the ordering contract between multiple custom evaluators explicit 
and self-describing rather than implicit in the declaration order of 
`job.autoscaler.metrics.custom-evaluators`, we can adopt the same design 
already approved in [FLIP-575: Scaling Executor Plugin SPI for Flink 
Autoscaler](https://cwiki.apache.org/confluence/display/FLINK/FLIP-575%3A+Scaling+Executor+Plugin+SPI+for+Flink+Autoscaler)
 and implemented for 
[ScalingExecutorPlugin.java‎](https://github.com/apache/flink-kubernetes-operator/pull/1085/changes#diff-8bc87f00339ad429c6d66b8b57d0d7ec8358e5e0d95f542b2f7d2a1a19271769):
 a `default int priority()` method on the SPI, with evaluators executed in 
ascending priority and the returned metrics merged left-to-right (lower 
priority first, higher priority wins on conflicts). This gives us:
   - a single, well-defined ordering contract that doesn't depend on the user's 
config-key ordering or on `ServiceLoader/PluginManager` discovery order,
   - parity and composability with `ScalingExecutorPlugin`, so both autoscaler 
extension points share the same mental model
   - a natural extension path for Option B above as `priority()` simply becomes 
the tie-breaker once we allow more than one evaluator per pipeline.
   
   The **Option B** has the following pro & cons:
   - Pros: consistent with an idiom Flink users already know, aligns with 
FLIP-514, expressive (multiple evaluators per job), and drops the `getName()` 
coupling that is awkward today.
   - Cons: bigger change than A.
   
   My preference would be to land **Option B** so we match FLIP-514 directly 
and don't ship a config surface we know we'll have to migrate away from.
   
   Thoughts?
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to