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 }