-
Notifications
You must be signed in to change notification settings - Fork 2
Description
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 Spec for 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 made.// Projections []Projection `json:"projections,omitempty"`
-
Target Resource Definition:
ManagedMetrichas an optionalTarget *GroupVersionKind.FederatedManagedMetrichas noTargetfield.
-
Status and Observation Structs:
- The
Statusobjects (MetricStatus,ManagedMetricStatus, etc.) and their nestedObservationstructs are all unique, making it difficult to create generic status-handling logic.
- The
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 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.var dimensions []v1alpha1.Dimension FederatedManagedHandler: Has zero support for processing user-defined projections/dimensions. It only works with status conditions.
Why This Is a Critical Problem
- Violates DRY Principle: We are maintaining four separate implementations for data extraction/ monitoring.
- High Maintenance Overhead: A bug fix or feature enhancement (e.g., improving JSONPath support Add support for map/slice dimensions for
MetricsandManagedMetrics#134) must be implemented and tested in up to four different places. - 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
FederatedMetriconly 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 toDimension, aligns with Dimensions vs. Projections #135. - Where possible, unify other common fields.
- Standardize on a single field for defining dimensions across all four metric types. A good candidate is
-
Create Shared Utility Packages/Functions:
- Dimension Extractor: Create a single, well-tested function that takes an
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.
- Dimension Extractor: Create a single, well-tested function that takes an
-
Refactor the Handlers:
- Remove all duplicated logic from the four handlers.
- Each handler's
Monitorfunction should become much simpler:- For each object, call the
DimensionExtractorutility to get its dimensions. - Construct the
DataPointand record the metric. (maybe we could extract this too)
- For each object, call the
Acceptance Criteria
- The
Specdefinitions 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.