k8s.io/kubernetes@v1.29.3/pkg/scheduler/metrics/metric_recorder.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package metrics
    18  
    19  import (
    20  	"time"
    21  
    22  	"k8s.io/component-base/metrics"
    23  )
    24  
    25  // MetricRecorder represents a metric recorder which takes action when the
    26  // metric Inc(), Dec() and Clear()
    27  type MetricRecorder interface {
    28  	Inc()
    29  	Dec()
    30  	Clear()
    31  }
    32  
    33  var _ MetricRecorder = &PendingPodsRecorder{}
    34  
    35  // PendingPodsRecorder is an implementation of MetricRecorder
    36  type PendingPodsRecorder struct {
    37  	recorder metrics.GaugeMetric
    38  }
    39  
    40  // NewActivePodsRecorder returns ActivePods in a Prometheus metric fashion
    41  func NewActivePodsRecorder() *PendingPodsRecorder {
    42  	return &PendingPodsRecorder{
    43  		recorder: ActivePods(),
    44  	}
    45  }
    46  
    47  // NewUnschedulablePodsRecorder returns UnschedulablePods in a Prometheus metric fashion
    48  func NewUnschedulablePodsRecorder() *PendingPodsRecorder {
    49  	return &PendingPodsRecorder{
    50  		recorder: UnschedulablePods(),
    51  	}
    52  }
    53  
    54  // NewBackoffPodsRecorder returns BackoffPods in a Prometheus metric fashion
    55  func NewBackoffPodsRecorder() *PendingPodsRecorder {
    56  	return &PendingPodsRecorder{
    57  		recorder: BackoffPods(),
    58  	}
    59  }
    60  
    61  // NewGatedPodsRecorder returns GatedPods in a Prometheus metric fashion
    62  func NewGatedPodsRecorder() *PendingPodsRecorder {
    63  	return &PendingPodsRecorder{
    64  		recorder: GatedPods(),
    65  	}
    66  }
    67  
    68  // Inc increases a metric counter by 1, in an atomic way
    69  func (r *PendingPodsRecorder) Inc() {
    70  	r.recorder.Inc()
    71  }
    72  
    73  // Dec decreases a metric counter by 1, in an atomic way
    74  func (r *PendingPodsRecorder) Dec() {
    75  	r.recorder.Dec()
    76  }
    77  
    78  // Clear set a metric counter to 0, in an atomic way
    79  func (r *PendingPodsRecorder) Clear() {
    80  	r.recorder.Set(float64(0))
    81  }
    82  
    83  // metric is the data structure passed in the buffer channel between the main framework thread
    84  // and the metricsRecorder goroutine.
    85  type metric struct {
    86  	metric      *metrics.HistogramVec
    87  	labelValues []string
    88  	value       float64
    89  }
    90  
    91  // MetricAsyncRecorder records metric in a separate goroutine to avoid overhead in the critical path.
    92  type MetricAsyncRecorder struct {
    93  	// bufferCh is a channel that serves as a metrics buffer before the metricsRecorder goroutine reports it.
    94  	bufferCh chan *metric
    95  	// if bufferSize is reached, incoming metrics will be discarded.
    96  	bufferSize int
    97  	// how often the recorder runs to flush the metrics.
    98  	interval time.Duration
    99  
   100  	// stopCh is used to stop the goroutine which periodically flushes metrics.
   101  	stopCh <-chan struct{}
   102  	// IsStoppedCh indicates whether the goroutine is stopped. It's used in tests only to make sure
   103  	// the metric flushing goroutine is stopped so that tests can collect metrics for verification.
   104  	IsStoppedCh chan struct{}
   105  }
   106  
   107  func NewMetricsAsyncRecorder(bufferSize int, interval time.Duration, stopCh <-chan struct{}) *MetricAsyncRecorder {
   108  	recorder := &MetricAsyncRecorder{
   109  		bufferCh:    make(chan *metric, bufferSize),
   110  		bufferSize:  bufferSize,
   111  		interval:    interval,
   112  		stopCh:      stopCh,
   113  		IsStoppedCh: make(chan struct{}),
   114  	}
   115  	go recorder.run()
   116  	return recorder
   117  }
   118  
   119  // ObservePluginDurationAsync observes the plugin_execution_duration_seconds metric.
   120  // The metric will be flushed to Prometheus asynchronously.
   121  func (r *MetricAsyncRecorder) ObservePluginDurationAsync(extensionPoint, pluginName, status string, value float64) {
   122  	newMetric := &metric{
   123  		metric:      PluginExecutionDuration,
   124  		labelValues: []string{pluginName, extensionPoint, status},
   125  		value:       value,
   126  	}
   127  	select {
   128  	case r.bufferCh <- newMetric:
   129  	default:
   130  	}
   131  }
   132  
   133  // run flushes buffered metrics into Prometheus every second.
   134  func (r *MetricAsyncRecorder) run() {
   135  	for {
   136  		select {
   137  		case <-r.stopCh:
   138  			close(r.IsStoppedCh)
   139  			return
   140  		default:
   141  		}
   142  		r.FlushMetrics()
   143  		time.Sleep(r.interval)
   144  	}
   145  }
   146  
   147  // FlushMetrics tries to clean up the bufferCh by reading at most bufferSize metrics.
   148  func (r *MetricAsyncRecorder) FlushMetrics() {
   149  	for i := 0; i < r.bufferSize; i++ {
   150  		select {
   151  		case m := <-r.bufferCh:
   152  			m.metric.WithLabelValues(m.labelValues...).Observe(m.value)
   153  		default:
   154  			return
   155  		}
   156  	}
   157  }