You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Whilst reviewing the codebase, working on #134, i noticed significant inconsistencies across our four metric CRDs (Metric, ManagedMetric, FederatedMetric, FederatedManagedMetric) and their corresponding handlers.
The current implementations are highly fragmented, with each metric type following its own distinct pattern for API definition and data processing. This has led to substantial code duplication, inconsistent user experience, and increased maintenance complexity.
This issue proposes a refactoring to unify these components, adhere to the DRY principle, and create a more robust and maintainable architecture.
Analysis of Inconsistencies
Analysis not complete, other inconsistencies may come up during refactoring.
We can group the observed problems into two main categories: 1. Inconsistent API Specs (api/v1alpha1/..._types.go)
The Spec for each of the four metric CRDs has diverged significantly.
Projection/Dimension Fields:
Metric and FederatedMetric use Projections []Projection.
ManagedMetric has an optionalTarget *GroupVersionKind.
FederatedManagedMetric has no Target field.
Status and Observation Structs:
The Status objects (MetricStatus, ManagedMetricStatus, etc.) and their nested Observation structs are all unique, making it difficult to create generic status-handling logic.
2. Divergent and Duplicated Handler Logic (internal/orchestrator/...handler.go)
The implementation logic within each handler shows a complete lack of shared code for common tasks.
Projection/Dimension Processing and Monitoring functions:
MetricHandler: Correctly supports multiple projections by grouping resources based on the unique combination of all projected values.
ManagedHandler: Implements its own logic for iterating through projections, which is different from MetricHandler.
Inconsistent User Experience: Users must learn different schemas and expect different behaviors depending on the metric CRD they use. For example, a user might be surprised that FederatedMetric only considers their first projection.
Technical Debt: The codebase is becoming increasingly complex and difficult to reason about, hindering future development.
Proposed Solution: A Unified Architecture
We should refactor towards a more composable and centralized architecture.
Unify the API Specs:
Standardize on a single field for defining dimensions across all four metric types. A good candidate is Dimensions []Projection, but renaming the struct to Dimension, aligns with Dimensions vs. Projections #135.
Where possible, unify other common fields.
Create Shared Utility Packages/Functions:
Dimension Extractor: Create a single, well-tested function that takes an unstructured.Unstructured object and a slice of dimension definitions ([]Projection). It should return a map[string]string of the extracted dimension values. This will be used by all current handlers, as this functionality should be shared.
Refactor the Handlers:
Remove all duplicated logic from the four handlers.
Each handler's Monitor function should become much simpler:
For each object, call the DimensionExtractor utility to get its dimensions.
Construct the DataPoint and record the metric. (maybe we could extract this too)
Acceptance Criteria
The Spec definitions for all four metric types are consistent, particularly for defining dimensions.
centralize logic for extracting Dimensions and adding tests.
All four handlers (MetricHandler, ManagedHandler, FederatedHandler, FederatedManagedHandler) are refactored to use the shared utilities.
Code duplication in the handlers is eliminated.
All existing functionality is preserved, and a user can now define multiple dimensions on any metric type with consistent behavior.
The limitation in FederatedHandler (only using the first projection) is removed.
Related: The PRs #106 and #134 are currently editing these files, adding functionality.
Description
Whilst reviewing the codebase, working on #134, i noticed significant inconsistencies across our four metric CRDs (
Metric,ManagedMetric,FederatedMetric,FederatedManagedMetric) and their corresponding handlers.The current implementations are highly fragmented, with each metric type following its own distinct pattern for API definition and data processing. This has led to substantial code duplication, inconsistent user experience, and increased maintenance complexity.
This issue proposes a refactoring to unify these components, adhere to the DRY principle, and create a more robust and maintainable architecture.
Analysis of Inconsistencies
Analysis not complete, other inconsistencies may come up during refactoring.
We can group the observed problems into two main categories:
1. Inconsistent API Specs (
api/v1alpha1/..._types.go)The
Specfor each of the four metric CRDs has diverged significantly.Projection/Dimension Fields:
MetricandFederatedMetricuseProjections []Projection.ManagedMetricin PR Add dimensions managedmetrics #106 is addingDimensions map[string]string, the PR Add support for map and slice dimensions for metrics and managedmetrics #138 (work in progress) is changing this toDimensions []Projection. The types ([]structvs.map) are inconsistent. The renaimng todimensionsis already tracked by Dimensions vs. Projections #135.FederatedManagedMetrichas no field for user-defined dimensions/projections at all, although preperatoins where mademetrics-operator/api/v1alpha1/federatedmanagedmetric_types.go
Line 38 in d1099b2
Target Resource Definition:
ManagedMetrichas an optionalTarget *GroupVersionKind.FederatedManagedMetrichas noTargetfield.Status and Observation Structs:
Statusobjects (MetricStatus,ManagedMetricStatus, etc.) and their nestedObservationstructs are all unique, making it difficult to create generic status-handling logic.2. Divergent and Duplicated Handler Logic (
internal/orchestrator/...handler.go)The implementation logic within each handler shows a complete lack of shared code for common tasks.
MetricHandler: Correctly supports multiple projections by grouping resources based on the unique combination of all projected values.ManagedHandler: Implements its own logic for iterating through projections, which is different fromMetricHandler.FederatedHandler: Contains a critical limitation, explicitly stating in a comment that it only processes the first projection. Similar to feature: Evaluate multiple projections #104, wich got fixed by Metric multi dimensions #107.metrics-operator/internal/orchestrator/federatedhandler.go
Line 77 in d1099b2
FederatedManagedHandler: Has zero support for processing user-defined projections/dimensions. It only works with status conditions.Why This Is a Critical Problem
MetricsandManagedMetrics#134) must be implemented and tested in up to four different places.FederatedMetriconly considers their first projection.Proposed Solution: A Unified Architecture
We should refactor towards a more composable and centralized architecture.
Unify the API Specs:
Dimensions []Projection, but renaming the struct toDimension, aligns with Dimensions vs. Projections #135.Create Shared Utility Packages/Functions:
unstructured.Unstructuredobject and a slice of dimension definitions ([]Projection). It should return amap[string]stringof the extracted dimension values. This will be used by all current handlers, as this functionality should be shared.Refactor the Handlers:
Monitorfunction should become much simpler:DimensionExtractorutility to get its dimensions.DataPointand record the metric. (maybe we could extract this too)Acceptance Criteria
Specdefinitions for all four metric types are consistent, particularly for defining dimensions.MetricHandler,ManagedHandler,FederatedHandler,FederatedManagedHandler) are refactored to use the shared utilities.FederatedHandler(only using the first projection) is removed.Related: The PRs #106 and #134 are currently editing these files, adding functionality.