k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/metrics/timing_ratio_histogram.go (about)

     1  /*
     2  Copyright 2022 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  	"context"
    21  	"sync"
    22  	"time"
    23  
    24  	compbasemetrics "k8s.io/component-base/metrics"
    25  	"k8s.io/klog/v2"
    26  )
    27  
    28  // TimingRatioHistogram is essentially a gauge for a ratio where the client
    29  // independently controls the numerator and denominator.
    30  // When scraped it produces a histogram of samples of the ratio
    31  // taken at the end of every nanosecond.
    32  // `*TimingRatioHistogram` implements both Registerable and RatioedGauge.
    33  type TimingRatioHistogram struct {
    34  	// The implementation is layered on TimingHistogram,
    35  	// adding the division by an occasionally adjusted denominator.
    36  
    37  	// Registerable is the registerable aspect.
    38  	// That is the registerable aspect of the underlying TimingHistogram.
    39  	compbasemetrics.Registerable
    40  
    41  	// timingRatioHistogramInner implements the RatioedGauge aspect.
    42  	timingRatioHistogramInner
    43  }
    44  
    45  // TimingRatioHistogramOpts is the constructor parameters of a TimingRatioHistogram.
    46  // The `TimingHistogramOpts.InitialValue` is the initial numerator.
    47  type TimingRatioHistogramOpts struct {
    48  	compbasemetrics.TimingHistogramOpts
    49  	InitialDenominator float64
    50  }
    51  
    52  // timingRatioHistogramInner implements the instrumentation aspect
    53  type timingRatioHistogramInner struct {
    54  	nowFunc         func() time.Time
    55  	getGaugeOfRatio func() Gauge
    56  	sync.Mutex
    57  	// access only with mutex locked
    58  	numerator, denominator float64
    59  }
    60  
    61  var _ RatioedGauge = &timingRatioHistogramInner{}
    62  var _ RatioedGauge = &TimingRatioHistogram{}
    63  var _ compbasemetrics.Registerable = &TimingRatioHistogram{}
    64  
    65  // NewTimingHistogram returns an object which is TimingHistogram-like. However, nothing
    66  // will be measured until the histogram is registered in at least one registry.
    67  func NewTimingRatioHistogram(opts *TimingRatioHistogramOpts) *TimingRatioHistogram {
    68  	return NewTestableTimingRatioHistogram(time.Now, opts)
    69  }
    70  
    71  // NewTestableTimingHistogram adds injection of the clock
    72  func NewTestableTimingRatioHistogram(nowFunc func() time.Time, opts *TimingRatioHistogramOpts) *TimingRatioHistogram {
    73  	//nolint:govet // copylocks: assignment copies lock value to ratioedOpts: k8s.io/component-base/metrics.TimingHistogramOpts contains sync.Once contains sync.Mutex
    74  	ratioedOpts := opts.TimingHistogramOpts
    75  	ratioedOpts.InitialValue /= opts.InitialDenominator
    76  	th := compbasemetrics.NewTestableTimingHistogram(nowFunc, &ratioedOpts)
    77  	return &TimingRatioHistogram{
    78  		Registerable: th,
    79  		timingRatioHistogramInner: timingRatioHistogramInner{
    80  			nowFunc:         nowFunc,
    81  			getGaugeOfRatio: func() Gauge { return th },
    82  			numerator:       opts.InitialValue,
    83  			denominator:     opts.InitialDenominator,
    84  		}}
    85  }
    86  
    87  func (trh *timingRatioHistogramInner) Set(numerator float64) {
    88  	trh.Lock()
    89  	defer trh.Unlock()
    90  	trh.numerator = numerator
    91  	ratio := numerator / trh.denominator
    92  	trh.getGaugeOfRatio().Set(ratio)
    93  }
    94  
    95  func (trh *timingRatioHistogramInner) Add(deltaNumerator float64) {
    96  	trh.Lock()
    97  	defer trh.Unlock()
    98  	numerator := trh.numerator + deltaNumerator
    99  	trh.numerator = numerator
   100  	ratio := numerator / trh.denominator
   101  	trh.getGaugeOfRatio().Set(ratio)
   102  }
   103  
   104  func (trh *timingRatioHistogramInner) Sub(deltaNumerator float64) {
   105  	trh.Add(-deltaNumerator)
   106  }
   107  
   108  func (trh *timingRatioHistogramInner) Inc() {
   109  	trh.Add(1)
   110  }
   111  
   112  func (trh *timingRatioHistogramInner) Dec() {
   113  	trh.Add(-1)
   114  }
   115  
   116  func (trh *timingRatioHistogramInner) SetToCurrentTime() {
   117  	trh.Set(float64(trh.nowFunc().Sub(time.Unix(0, 0))))
   118  }
   119  
   120  func (trh *timingRatioHistogramInner) SetDenominator(denominator float64) {
   121  	trh.Lock()
   122  	defer trh.Unlock()
   123  	trh.denominator = denominator
   124  	ratio := trh.numerator / denominator
   125  	trh.getGaugeOfRatio().Set(ratio)
   126  }
   127  
   128  // WithContext allows the normal TimingHistogram metric to pass in context.
   129  // The context is no-op at the current level of development.
   130  func (trh *timingRatioHistogramInner) WithContext(ctx context.Context) RatioedGauge {
   131  	return trh
   132  }
   133  
   134  // TimingRatioHistogramVec is a collection of TimingRatioHistograms that differ
   135  // only in label values.
   136  // `*TimingRatioHistogramVec` implements both Registerable and RatioedGaugeVec.
   137  type TimingRatioHistogramVec struct {
   138  	// promote only the Registerable methods
   139  	compbasemetrics.Registerable
   140  	// delegate is TimingHistograms of the ratio
   141  	delegate compbasemetrics.GaugeVecMetric
   142  }
   143  
   144  var _ RatioedGaugeVec = &TimingRatioHistogramVec{}
   145  var _ compbasemetrics.Registerable = &TimingRatioHistogramVec{}
   146  
   147  // NewTimingHistogramVec constructs a new vector.
   148  // `opts.InitialValue` is the initial ratio, but this applies
   149  // only for the tiny period of time until NewForLabelValuesSafe sets
   150  // the ratio based on the given initial numerator and denominator.
   151  // Thus there is a tiny splinter of time during member construction when
   152  // its underlying TimingHistogram is given the initial numerator rather than
   153  // the initial ratio (which is obviously a non-issue when both are zero).
   154  // Note the difficulties associated with extracting a member
   155  // before registering the vector.
   156  func NewTimingRatioHistogramVec(opts *compbasemetrics.TimingHistogramOpts, labelNames ...string) *TimingRatioHistogramVec {
   157  	return NewTestableTimingRatioHistogramVec(time.Now, opts, labelNames...)
   158  }
   159  
   160  // NewTestableTimingHistogramVec adds injection of the clock.
   161  func NewTestableTimingRatioHistogramVec(nowFunc func() time.Time, opts *compbasemetrics.TimingHistogramOpts, labelNames ...string) *TimingRatioHistogramVec {
   162  	delegate := compbasemetrics.NewTestableTimingHistogramVec(nowFunc, opts, labelNames)
   163  	return &TimingRatioHistogramVec{
   164  		Registerable: delegate,
   165  		delegate:     delegate,
   166  	}
   167  }
   168  
   169  func (v *TimingRatioHistogramVec) metrics() Registerables {
   170  	return Registerables{v}
   171  }
   172  
   173  // NewForLabelValuesChecked will return an error if this vec is not hidden and not yet registered
   174  // or there is a syntactic problem with the labelValues.
   175  func (v *TimingRatioHistogramVec) NewForLabelValuesChecked(initialNumerator, initialDenominator float64, labelValues []string) (RatioedGauge, error) {
   176  	underMember, err := v.delegate.WithLabelValuesChecked(labelValues...)
   177  	if err != nil {
   178  		return noopRatioed{}, err
   179  	}
   180  	underMember.Set(initialNumerator / initialDenominator)
   181  	return &timingRatioHistogramInner{
   182  		getGaugeOfRatio: func() Gauge { return underMember },
   183  		numerator:       initialNumerator,
   184  		denominator:     initialDenominator,
   185  	}, nil
   186  }
   187  
   188  // NewForLabelValuesSafe is the same as NewForLabelValuesChecked in cases where that does not
   189  // return an error.  When the unsafe version returns an error due to the vector not being
   190  // registered yet, the safe version returns an object that implements its methods
   191  // by looking up the relevant vector member in each call (thus getting a non-noop after registration).
   192  // In the other error cases the object returned here is a noop.
   193  func (v *TimingRatioHistogramVec) NewForLabelValuesSafe(initialNumerator, initialDenominator float64, labelValues []string) RatioedGauge {
   194  	tro, err := v.NewForLabelValuesChecked(initialNumerator, initialDenominator, labelValues)
   195  	if err == nil {
   196  		klog.V(3).InfoS("TimingRatioHistogramVec.NewForLabelValuesSafe hit the efficient case", "fqName", v.FQName(), "labelValues", labelValues)
   197  		return tro
   198  	}
   199  	if !compbasemetrics.ErrIsNotRegistered(err) {
   200  		klog.ErrorS(err, "Failed to extract TimingRatioHistogramVec member, using noop instead", "vectorname", v.FQName(), "labelValues", labelValues)
   201  		return tro
   202  	}
   203  	klog.V(3).InfoS("TimingRatioHistogramVec.NewForLabelValuesSafe hit the inefficient case", "fqName", v.FQName(), "labelValues", labelValues)
   204  	// At this point we know v.NewForLabelValuesChecked(..) returns a permanent noop,
   205  	// which we precisely want to avoid using.  Instead, make our own gauge that
   206  	// fetches the element on every Set.
   207  	return &timingRatioHistogramInner{
   208  		getGaugeOfRatio: func() Gauge { return v.delegate.WithLabelValues(labelValues...) },
   209  		numerator:       initialNumerator,
   210  		denominator:     initialDenominator,
   211  	}
   212  }
   213  
   214  type noopRatioed struct{}
   215  
   216  func (noopRatioed) Set(float64)            {}
   217  func (noopRatioed) Add(float64)            {}
   218  func (noopRatioed) Sub(float64)            {}
   219  func (noopRatioed) Inc()                   {}
   220  func (noopRatioed) Dec()                   {}
   221  func (noopRatioed) SetToCurrentTime()      {}
   222  func (noopRatioed) SetDenominator(float64) {}
   223  
   224  func (v *TimingRatioHistogramVec) Reset() {
   225  	v.delegate.Reset()
   226  }