2525 spareCapacity * prometheus.GaugeVec
2626 requiredCapacity * prometheus.GaugeVec
2727 kvCacheTokensUsed * prometheus.GaugeVec
28- kvCacheTokensTotal * prometheus.GaugeVec
28+ kvCacheTokensCapacity * prometheus.GaugeVec
2929
3030 // controllerInstance stores the optional controller instance identifier.
3131 // When set, it's added as a label to all emitted metrics.
@@ -51,9 +51,9 @@ func InitMetrics(registry prometheus.Registerer) error {
5151 scalingLabels := []string {constants .LabelVariantName , constants .LabelNamespace , constants .LabelDirection , constants .LabelReason }
5252 // modelLabels: variant_name + namespace only (no accelerator_type) for model-level and token metrics
5353 modelLabels := []string {constants .LabelVariantName , constants .LabelNamespace }
54- // requiredCapacityLabels: model labels + analyzer_version to disambiguate V1 (binary)
55- // vs V2 (continuous tokens) units of the wva_required_capacity gauge
56- requiredCapacityLabels := []string {constants .LabelVariantName , constants .LabelNamespace , constants .LabelAnalyzerVersion }
54+ // requiredCapacityLabels: model labels + "unit" to disambiguate V1 (binary 0/1 )
55+ // vs V2 (continuous token demand) values of the wva_required_capacity gauge
56+ requiredCapacityLabels := []string {constants .LabelVariantName , constants .LabelNamespace , constants .LabelUnit }
5757
5858 if controllerInstance != "" {
5959 baseLabels = append (baseLabels , constants .LabelControllerInstance )
@@ -93,35 +93,35 @@ func InitMetrics(registry prometheus.Registerer) error {
9393 saturationUtilization = prometheus .NewGaugeVec (
9494 prometheus.GaugeOpts {
9595 Name : constants .WVASaturationUtilization ,
96- Help : "Per-variant utilization ratio (0.0-1.0) from saturation analysis" ,
96+ Help : "Per-variant utilization ratio (0.0-1.0) from saturation analysis. V1 path: mean of per-replica KV-cache-usage fractions (matches the per-replica threshold V1 checks). V2 path: TotalDemand / TotalCapacity from the analyzer result. Numerically equivalent for uniform-capacity replicas; V2 is capacity-weighted for mixed-capacity cases. " ,
9797 },
9898 baseLabels ,
9999 )
100100 spareCapacity = prometheus .NewGaugeVec (
101101 prometheus.GaugeOpts {
102102 Name : constants .WVASpareCapacity ,
103- Help : "Per-variant spare capacity (0.0-1.0) from saturation analysis" ,
103+ Help : "Per-variant spare KV-cache capacity (0.0-1.0) from saturation analysis. V1 path: threshold-relative spare (kvCacheThreshold - avg KV usage). V2 path: 1.0 - utilization. " ,
104104 },
105105 baseLabels ,
106106 )
107107 requiredCapacity = prometheus .NewGaugeVec (
108108 prometheus.GaugeOpts {
109109 Name : constants .WVARequiredCapacity ,
110- Help : "Model-level required capacity; >0 indicates scale-up needed. Use the analyzer_version label to distinguish units (V1: binary 0/1, V2: continuous token demand)." ,
110+ Help : fmt . Sprintf ( "Model-level required capacity; >0 indicates scale-up needed. Use the %q label to interpret the value: %q → 0/1 scale-up signal (V1), %q → token demand (V2 )." , constants . LabelUnit , constants . UnitBinary , constants . UnitContinuous ) ,
111111 },
112112 requiredCapacityLabels ,
113113 )
114114 kvCacheTokensUsed = prometheus .NewGaugeVec (
115115 prometheus.GaugeOpts {
116116 Name : constants .WVAKvCacheTokensUsed ,
117- Help : "Total KV cache tokens currently in use across all replicas of a variant" ,
117+ Help : "Total KV cache tokens currently in use across all replicas of a variant (sum of vLLM TokensInUse). " ,
118118 },
119119 modelLabels ,
120120 )
121- kvCacheTokensTotal = prometheus .NewGaugeVec (
121+ kvCacheTokensCapacity = prometheus .NewGaugeVec (
122122 prometheus.GaugeOpts {
123- Name : constants .WVAKvCacheTokensTotal ,
124- Help : "Total KV cache token capacity across all replicas of a variant" ,
123+ Name : constants .WVAKvCacheTokensCapacity ,
124+ Help : "Total KV cache token capacity across all replicas of a variant (sum of vLLM TotalKvCapacityTokens). " ,
125125 },
126126 modelLabels ,
127127 )
@@ -151,8 +151,8 @@ func InitMetrics(registry prometheus.Registerer) error {
151151 if err := registry .Register (kvCacheTokensUsed ); err != nil {
152152 return fmt .Errorf ("failed to register kvCacheTokensUsed metric: %w" , err )
153153 }
154- if err := registry .Register (kvCacheTokensTotal ); err != nil {
155- return fmt .Errorf ("failed to register kvCacheTokensTotal metric: %w" , err )
154+ if err := registry .Register (kvCacheTokensCapacity ); err != nil {
155+ return fmt .Errorf ("failed to register kvCacheTokensCapacity metric: %w" , err )
156156 }
157157
158158 return nil
@@ -230,16 +230,16 @@ func (m *MetricsEmitter) EmitReplicaMetrics(ctx context.Context, va *llmdOptv1al
230230}
231231
232232// EmitSaturationMetrics emits saturation analysis and KV cache capacity metrics.
233- // analyzerVersion ("v1 " or "v2 ") is used as a label on wva_required_capacity to
234- // disambiguate the units of the required value (V1: binary, V2: continuous tokens) .
233+ // requiredCapacityUnit ("binary " or "continuous ") is used as the "unit" label on
234+ // wva_required_capacity to describe how the value should be interpreted .
235235func (m * MetricsEmitter ) EmitSaturationMetrics (
236236 ctx context.Context ,
237- variantName , namespace , acceleratorType , analyzerVersion string ,
237+ variantName , namespace , acceleratorType , requiredCapacityUnit string ,
238238 utilization , spare , required float64 ,
239- kvTokensUsed , kvTokensTotal int64 ,
239+ kvTokensUsed , kvTokensCapacity int64 ,
240240) error {
241241 if saturationUtilization == nil || spareCapacity == nil || requiredCapacity == nil ||
242- kvCacheTokensUsed == nil || kvCacheTokensTotal == nil {
242+ kvCacheTokensUsed == nil || kvCacheTokensCapacity == nil {
243243 return errors .New ("saturation metrics not initialized" )
244244 }
245245
@@ -253,9 +253,9 @@ func (m *MetricsEmitter) EmitSaturationMetrics(
253253 constants .LabelNamespace : namespace ,
254254 }
255255 requiredLabels := prometheus.Labels {
256- constants .LabelVariantName : variantName ,
257- constants .LabelNamespace : namespace ,
258- constants .LabelAnalyzerVersion : analyzerVersion ,
256+ constants .LabelVariantName : variantName ,
257+ constants .LabelNamespace : namespace ,
258+ constants .LabelUnit : requiredCapacityUnit ,
259259 }
260260
261261 if controllerInstance != "" {
@@ -268,7 +268,7 @@ func (m *MetricsEmitter) EmitSaturationMetrics(
268268 spareCapacity .With (accelLabels ).Set (spare )
269269 requiredCapacity .With (requiredLabels ).Set (required )
270270 kvCacheTokensUsed .With (modelLabels ).Set (float64 (kvTokensUsed ))
271- kvCacheTokensTotal .With (modelLabels ).Set (float64 (kvTokensTotal ))
271+ kvCacheTokensCapacity .With (modelLabels ).Set (float64 (kvTokensCapacity ))
272272
273273 return nil
274274}
@@ -280,8 +280,42 @@ func (m *MetricsEmitter) EmitSaturationMetrics(
280280// TODO: wire this from the controller's VariantAutoscaling delete handler / finalizer.
281281// Until that wiring exists, deleted VAs leave their last-emitted metric values in the
282282// registry indefinitely.
283- func (m * MetricsEmitter ) DeleteSaturationMetrics (variantName , namespace , acceleratorType , analyzerVersion string ) {
284- if saturationUtilization == nil {
283+ // DeleteSaturationMetricsForVariant removes all saturation metric series for the
284+ // given (variant, namespace) regardless of accelerator type or unit label. Uses
285+ // Prometheus DeletePartialMatch so callers don't need to know every label value.
286+ //
287+ // Intended for two scenarios:
288+ // - The optimization cycle produced no decision for this variant — existing
289+ // series would otherwise persist with stale values until Prometheus' default
290+ // 5-minute staleness marker fires. Calling this ensures dashboards show a
291+ // gap (honest "no fresh data") rather than stale values.
292+ // - A VA is being removed (delete handler / finalizer) and the caller doesn't
293+ // want to reconstruct the exact emitted label set.
294+ func (m * MetricsEmitter ) DeleteSaturationMetricsForVariant (variantName , namespace string ) {
295+ if saturationUtilization == nil || spareCapacity == nil || requiredCapacity == nil ||
296+ kvCacheTokensUsed == nil || kvCacheTokensCapacity == nil {
297+ return
298+ }
299+ match := prometheus.Labels {
300+ constants .LabelVariantName : variantName ,
301+ constants .LabelNamespace : namespace ,
302+ }
303+ if controllerInstance != "" {
304+ match [constants .LabelControllerInstance ] = controllerInstance
305+ }
306+ saturationUtilization .DeletePartialMatch (match )
307+ spareCapacity .DeletePartialMatch (match )
308+ requiredCapacity .DeletePartialMatch (match )
309+ kvCacheTokensUsed .DeletePartialMatch (match )
310+ kvCacheTokensCapacity .DeletePartialMatch (match )
311+ }
312+
313+ func (m * MetricsEmitter ) DeleteSaturationMetrics (variantName , namespace , acceleratorType , requiredCapacityUnit string ) {
314+ // No-op if any metric hasn't been initialized. InitMetrics registers all
315+ // five together, so either all are non-nil or all are nil — checking every
316+ // var defends against a future code path that partially initializes them.
317+ if saturationUtilization == nil || spareCapacity == nil || requiredCapacity == nil ||
318+ kvCacheTokensUsed == nil || kvCacheTokensCapacity == nil {
285319 return
286320 }
287321 accelLabels := prometheus.Labels {
@@ -294,9 +328,9 @@ func (m *MetricsEmitter) DeleteSaturationMetrics(variantName, namespace, acceler
294328 constants .LabelNamespace : namespace ,
295329 }
296330 requiredLabels := prometheus.Labels {
297- constants .LabelVariantName : variantName ,
298- constants .LabelNamespace : namespace ,
299- constants .LabelAnalyzerVersion : analyzerVersion ,
331+ constants .LabelVariantName : variantName ,
332+ constants .LabelNamespace : namespace ,
333+ constants .LabelUnit : requiredCapacityUnit ,
300334 }
301335 if controllerInstance != "" {
302336 accelLabels [constants .LabelControllerInstance ] = controllerInstance
@@ -307,5 +341,5 @@ func (m *MetricsEmitter) DeleteSaturationMetrics(variantName, namespace, acceler
307341 spareCapacity .Delete (accelLabels )
308342 requiredCapacity .Delete (requiredLabels )
309343 kvCacheTokensUsed .Delete (modelLabels )
310- kvCacheTokensTotal .Delete (modelLabels )
344+ kvCacheTokensCapacity .Delete (modelLabels )
311345}
0 commit comments