k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/metrics/metrics.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  	"context"
    21  	"strconv"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	epmetrics "k8s.io/apiserver/pkg/endpoints/metrics"
    27  	apirequest "k8s.io/apiserver/pkg/endpoints/request"
    28  	compbasemetrics "k8s.io/component-base/metrics"
    29  	"k8s.io/component-base/metrics/legacyregistry"
    30  	basemetricstestutil "k8s.io/component-base/metrics/testutil"
    31  )
    32  
    33  const (
    34  	namespace = "apiserver"
    35  	subsystem = "flowcontrol"
    36  )
    37  
    38  const (
    39  	requestKind         = "request_kind"
    40  	priorityLevel       = "priority_level"
    41  	flowSchema          = "flow_schema"
    42  	phase               = "phase"
    43  	LabelNamePhase      = "phase"
    44  	LabelValueWaiting   = "waiting"
    45  	LabelValueExecuting = "executing"
    46  )
    47  
    48  var (
    49  	queueLengthBuckets            = []float64{0, 10, 25, 50, 100, 250, 500, 1000}
    50  	requestDurationSecondsBuckets = []float64{0, 0.005, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 15, 30}
    51  )
    52  
    53  var registerMetrics sync.Once
    54  
    55  // Register all metrics.
    56  func Register() {
    57  	registerMetrics.Do(func() {
    58  		for _, metric := range metrics {
    59  			legacyregistry.MustRegister(metric)
    60  		}
    61  	})
    62  }
    63  
    64  type resettable interface {
    65  	Reset()
    66  }
    67  
    68  // Reset all resettable metrics to zero
    69  func Reset() {
    70  	for _, metric := range metrics {
    71  		if rm, ok := metric.(resettable); ok {
    72  			rm.Reset()
    73  		}
    74  	}
    75  }
    76  
    77  // GatherAndCompare the given metrics with the given Prometheus syntax expected value
    78  func GatherAndCompare(expected string, metricNames ...string) error {
    79  	return basemetricstestutil.GatherAndCompare(legacyregistry.DefaultGatherer, strings.NewReader(expected), metricNames...)
    80  }
    81  
    82  // Registerables is a slice of Registerable
    83  type Registerables []compbasemetrics.Registerable
    84  
    85  // Append adds more
    86  func (rs Registerables) Append(more ...compbasemetrics.Registerable) Registerables {
    87  	return append(rs, more...)
    88  }
    89  
    90  var (
    91  	apiserverRejectedRequestsTotal = compbasemetrics.NewCounterVec(
    92  		&compbasemetrics.CounterOpts{
    93  			Namespace:      namespace,
    94  			Subsystem:      subsystem,
    95  			Name:           "rejected_requests_total",
    96  			Help:           "Number of requests rejected by API Priority and Fairness subsystem",
    97  			StabilityLevel: compbasemetrics.BETA,
    98  		},
    99  		[]string{priorityLevel, flowSchema, "reason"},
   100  	)
   101  	apiserverDispatchedRequestsTotal = compbasemetrics.NewCounterVec(
   102  		&compbasemetrics.CounterOpts{
   103  			Namespace:      namespace,
   104  			Subsystem:      subsystem,
   105  			Name:           "dispatched_requests_total",
   106  			Help:           "Number of requests executed by API Priority and Fairness subsystem",
   107  			StabilityLevel: compbasemetrics.BETA,
   108  		},
   109  		[]string{priorityLevel, flowSchema},
   110  	)
   111  	// PriorityLevelExecutionSeatsGaugeVec creates observers of seats occupied throughout execution for priority levels
   112  	PriorityLevelExecutionSeatsGaugeVec = NewTimingRatioHistogramVec(
   113  		&compbasemetrics.TimingHistogramOpts{
   114  			Namespace: namespace,
   115  			Subsystem: subsystem,
   116  			Name:      "priority_level_seat_utilization",
   117  			Help:      "Observations, at the end of every nanosecond, of utilization of seats for any stage of execution (but only initial stage for WATCHes)",
   118  			// Buckets for both 0.99 and 1.0 mean PromQL's histogram_quantile will reveal saturation
   119  			Buckets:        []float64{0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1},
   120  			ConstLabels:    map[string]string{phase: "executing"},
   121  			StabilityLevel: compbasemetrics.ALPHA,
   122  		},
   123  		priorityLevel,
   124  	)
   125  	// PriorityLevelConcurrencyGaugeVec creates gauges of concurrency broken down by phase, priority level
   126  	PriorityLevelConcurrencyGaugeVec = NewTimingRatioHistogramVec(
   127  		&compbasemetrics.TimingHistogramOpts{
   128  			Namespace: namespace,
   129  			Subsystem: subsystem,
   130  			Name:      "priority_level_request_utilization",
   131  			Help:      "Observations, at the end of every nanosecond, of number of requests (as a fraction of the relevant limit) waiting or in any stage of execution (but only initial stage for WATCHes)",
   132  			// For executing: the denominator will be seats, so this metric will skew low.
   133  			// For waiting: total queue capacity is generally quite generous, so this metric will skew low.
   134  			Buckets:        []float64{0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.25, 0.5, 0.75, 1},
   135  			StabilityLevel: compbasemetrics.ALPHA,
   136  		},
   137  		LabelNamePhase, priorityLevel,
   138  	)
   139  	// readWriteConcurrencyGaugeVec creates ratioed gauges of requests/limit broken down by phase and mutating vs readonly
   140  	readWriteConcurrencyGaugeVec = NewTimingRatioHistogramVec(
   141  		&compbasemetrics.TimingHistogramOpts{
   142  			Namespace: namespace,
   143  			Subsystem: subsystem,
   144  			Name:      "read_vs_write_current_requests",
   145  			Help:      "Observations, at the end of every nanosecond, of the number of requests (as a fraction of the relevant limit) waiting or in regular stage of execution",
   146  			// This metric will skew low for the same reason as the priority level metrics
   147  			// and also because APF has a combined limit for mutating and readonly.
   148  			Buckets:        []float64{0, 0.001, 0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1},
   149  			StabilityLevel: compbasemetrics.ALPHA,
   150  		},
   151  		LabelNamePhase, requestKind,
   152  	)
   153  	apiserverCurrentR = compbasemetrics.NewGaugeVec(
   154  		&compbasemetrics.GaugeOpts{
   155  			Namespace:      namespace,
   156  			Subsystem:      subsystem,
   157  			Name:           "current_r",
   158  			Help:           "R(time of last change)",
   159  			StabilityLevel: compbasemetrics.ALPHA,
   160  		},
   161  		[]string{priorityLevel},
   162  	)
   163  	apiserverDispatchR = compbasemetrics.NewGaugeVec(
   164  		&compbasemetrics.GaugeOpts{
   165  			Namespace:      namespace,
   166  			Subsystem:      subsystem,
   167  			Name:           "dispatch_r",
   168  			Help:           "R(time of last dispatch)",
   169  			StabilityLevel: compbasemetrics.ALPHA,
   170  		},
   171  		[]string{priorityLevel},
   172  	)
   173  	apiserverLatestS = compbasemetrics.NewGaugeVec(
   174  		&compbasemetrics.GaugeOpts{
   175  			Namespace:      namespace,
   176  			Subsystem:      subsystem,
   177  			Name:           "latest_s",
   178  			Help:           "S(most recently dispatched request)",
   179  			StabilityLevel: compbasemetrics.ALPHA,
   180  		},
   181  		[]string{priorityLevel},
   182  	)
   183  	apiserverNextSBounds = compbasemetrics.NewGaugeVec(
   184  		&compbasemetrics.GaugeOpts{
   185  			Namespace:      namespace,
   186  			Subsystem:      subsystem,
   187  			Name:           "next_s_bounds",
   188  			Help:           "min and max, over queues, of S(oldest waiting request in queue)",
   189  			StabilityLevel: compbasemetrics.ALPHA,
   190  		},
   191  		[]string{priorityLevel, "bound"},
   192  	)
   193  	apiserverNextDiscountedSBounds = compbasemetrics.NewGaugeVec(
   194  		&compbasemetrics.GaugeOpts{
   195  			Namespace:      namespace,
   196  			Subsystem:      subsystem,
   197  			Name:           "next_discounted_s_bounds",
   198  			Help:           "min and max, over queues, of S(oldest waiting request in queue) - estimated work in progress",
   199  			StabilityLevel: compbasemetrics.ALPHA,
   200  		},
   201  		[]string{priorityLevel, "bound"},
   202  	)
   203  	apiserverCurrentInqueueRequests = compbasemetrics.NewGaugeVec(
   204  		&compbasemetrics.GaugeOpts{
   205  			Namespace:      namespace,
   206  			Subsystem:      subsystem,
   207  			Name:           "current_inqueue_requests",
   208  			Help:           "Number of requests currently pending in queues of the API Priority and Fairness subsystem",
   209  			StabilityLevel: compbasemetrics.BETA,
   210  		},
   211  		[]string{priorityLevel, flowSchema},
   212  	)
   213  	apiserverCurrentInqueueSeats = compbasemetrics.NewGaugeVec(
   214  		&compbasemetrics.GaugeOpts{
   215  			Namespace:      namespace,
   216  			Subsystem:      subsystem,
   217  			Name:           "current_inqueue_seats",
   218  			Help:           "Number of seats currently pending in queues of the API Priority and Fairness subsystem",
   219  			StabilityLevel: compbasemetrics.ALPHA,
   220  		},
   221  		[]string{priorityLevel, flowSchema},
   222  	)
   223  	apiserverRequestQueueLength = compbasemetrics.NewHistogramVec(
   224  		&compbasemetrics.HistogramOpts{
   225  			Namespace:      namespace,
   226  			Subsystem:      subsystem,
   227  			Name:           "request_queue_length_after_enqueue",
   228  			Help:           "Length of queue in the API Priority and Fairness subsystem, as seen by each request after it is enqueued",
   229  			Buckets:        queueLengthBuckets,
   230  			StabilityLevel: compbasemetrics.ALPHA,
   231  		},
   232  		[]string{priorityLevel, flowSchema},
   233  	)
   234  	apiserverRequestConcurrencyLimit = compbasemetrics.NewGaugeVec(
   235  		&compbasemetrics.GaugeOpts{
   236  			Namespace: namespace,
   237  			Subsystem: subsystem,
   238  			Name:      "request_concurrency_limit",
   239  			Help:      "Nominal number of execution seats configured for each priority level",
   240  			// Remove this metric once all suppported releases have the equal nominal_limit_seats metric
   241  			DeprecatedVersion: "1.30.0",
   242  			StabilityLevel:    compbasemetrics.ALPHA,
   243  		},
   244  		[]string{priorityLevel},
   245  	)
   246  	apiserverCurrentExecutingRequests = compbasemetrics.NewGaugeVec(
   247  		&compbasemetrics.GaugeOpts{
   248  			Namespace:      namespace,
   249  			Subsystem:      subsystem,
   250  			Name:           "current_executing_requests",
   251  			Help:           "Number of requests in initial (for a WATCH) or any (for a non-WATCH) execution stage in the API Priority and Fairness subsystem",
   252  			StabilityLevel: compbasemetrics.BETA,
   253  		},
   254  		[]string{priorityLevel, flowSchema},
   255  	)
   256  	apiserverCurrentExecutingSeats = compbasemetrics.NewGaugeVec(
   257  		&compbasemetrics.GaugeOpts{
   258  			Namespace:      namespace,
   259  			Subsystem:      subsystem,
   260  			Name:           "current_executing_seats",
   261  			Help:           "Concurrency (number of seats) occupied by the currently executing (initial stage for a WATCH, any stage otherwise) requests in the API Priority and Fairness subsystem",
   262  			StabilityLevel: compbasemetrics.BETA,
   263  		},
   264  		[]string{priorityLevel, flowSchema},
   265  	)
   266  	apiserverRequestConcurrencyInUse = compbasemetrics.NewGaugeVec(
   267  		&compbasemetrics.GaugeOpts{
   268  			Namespace: namespace,
   269  			Subsystem: subsystem,
   270  			Name:      "request_concurrency_in_use",
   271  			Help:      "Concurrency (number of seats) occupied by the currently executing (initial stage for a WATCH, any stage otherwise) requests in the API Priority and Fairness subsystem",
   272  			// Remove this metric once all suppported releases have the equal current_executing_seats metric
   273  			DeprecatedVersion: "1.31.0",
   274  			StabilityLevel:    compbasemetrics.ALPHA,
   275  		},
   276  		[]string{priorityLevel, flowSchema},
   277  	)
   278  	apiserverRequestWaitingSeconds = compbasemetrics.NewHistogramVec(
   279  		&compbasemetrics.HistogramOpts{
   280  			Namespace:      namespace,
   281  			Subsystem:      subsystem,
   282  			Name:           "request_wait_duration_seconds",
   283  			Help:           "Length of time a request spent waiting in its queue",
   284  			Buckets:        requestDurationSecondsBuckets,
   285  			StabilityLevel: compbasemetrics.BETA,
   286  		},
   287  		[]string{priorityLevel, flowSchema, "execute"},
   288  	)
   289  	apiserverRequestExecutionSeconds = compbasemetrics.NewHistogramVec(
   290  		&compbasemetrics.HistogramOpts{
   291  			Namespace:      namespace,
   292  			Subsystem:      subsystem,
   293  			Name:           "request_execution_seconds",
   294  			Help:           "Duration of initial stage (for a WATCH) or any (for a non-WATCH) stage of request execution in the API Priority and Fairness subsystem",
   295  			Buckets:        requestDurationSecondsBuckets,
   296  			StabilityLevel: compbasemetrics.ALPHA,
   297  		},
   298  		[]string{priorityLevel, flowSchema, "type"},
   299  	)
   300  	watchCountSamples = compbasemetrics.NewHistogramVec(
   301  		&compbasemetrics.HistogramOpts{
   302  			Namespace:      namespace,
   303  			Subsystem:      subsystem,
   304  			Name:           "watch_count_samples",
   305  			Help:           "count of watchers for mutating requests in API Priority and Fairness",
   306  			Buckets:        []float64{0, 1, 10, 100, 1000, 10000},
   307  			StabilityLevel: compbasemetrics.ALPHA,
   308  		},
   309  		[]string{priorityLevel, flowSchema},
   310  	)
   311  	apiserverEpochAdvances = compbasemetrics.NewCounterVec(
   312  		&compbasemetrics.CounterOpts{
   313  			Namespace:      namespace,
   314  			Subsystem:      subsystem,
   315  			Name:           "epoch_advance_total",
   316  			Help:           "Number of times the queueset's progress meter jumped backward",
   317  			StabilityLevel: compbasemetrics.ALPHA,
   318  		},
   319  		[]string{priorityLevel, "success"},
   320  	)
   321  	apiserverWorkEstimatedSeats = compbasemetrics.NewHistogramVec(
   322  		&compbasemetrics.HistogramOpts{
   323  			Namespace: namespace,
   324  			Subsystem: subsystem,
   325  			Name:      "work_estimated_seats",
   326  			Help:      "Number of estimated seats (maximum of initial and final seats) associated with requests in API Priority and Fairness",
   327  			// the upper bound comes from the maximum number of seats a request
   328  			// can occupy which is currently set at 10.
   329  			Buckets:        []float64{1, 2, 4, 10},
   330  			StabilityLevel: compbasemetrics.ALPHA,
   331  		},
   332  		[]string{priorityLevel, flowSchema},
   333  	)
   334  	apiserverDispatchWithNoAccommodation = compbasemetrics.NewCounterVec(
   335  		&compbasemetrics.CounterOpts{
   336  			Namespace:      namespace,
   337  			Subsystem:      subsystem,
   338  			Name:           "request_dispatch_no_accommodation_total",
   339  			Help:           "Number of times a dispatch attempt resulted in a non accommodation due to lack of available seats",
   340  			StabilityLevel: compbasemetrics.ALPHA,
   341  		},
   342  		[]string{priorityLevel, flowSchema},
   343  	)
   344  	apiserverNominalConcurrencyLimits = compbasemetrics.NewGaugeVec(
   345  		&compbasemetrics.GaugeOpts{
   346  			Namespace:      namespace,
   347  			Subsystem:      subsystem,
   348  			Name:           "nominal_limit_seats",
   349  			Help:           "Nominal number of execution seats configured for each priority level",
   350  			StabilityLevel: compbasemetrics.BETA,
   351  		},
   352  		[]string{priorityLevel},
   353  	)
   354  	apiserverMinimumConcurrencyLimits = compbasemetrics.NewGaugeVec(
   355  		&compbasemetrics.GaugeOpts{
   356  			Namespace:      namespace,
   357  			Subsystem:      subsystem,
   358  			Name:           "lower_limit_seats",
   359  			Help:           "Configured lower bound on number of execution seats available to each priority level",
   360  			StabilityLevel: compbasemetrics.ALPHA,
   361  		},
   362  		[]string{priorityLevel},
   363  	)
   364  	apiserverMaximumConcurrencyLimits = compbasemetrics.NewGaugeVec(
   365  		&compbasemetrics.GaugeOpts{
   366  			Namespace:      namespace,
   367  			Subsystem:      subsystem,
   368  			Name:           "upper_limit_seats",
   369  			Help:           "Configured upper bound on number of execution seats available to each priority level",
   370  			StabilityLevel: compbasemetrics.ALPHA,
   371  		},
   372  		[]string{priorityLevel},
   373  	)
   374  	ApiserverSeatDemands = NewTimingRatioHistogramVec(
   375  		&compbasemetrics.TimingHistogramOpts{
   376  			Namespace: namespace,
   377  			Subsystem: subsystem,
   378  			Name:      "demand_seats",
   379  			Help:      "Observations, at the end of every nanosecond, of (the number of seats each priority level could use) / (nominal number of seats for that level)",
   380  			// Rationale for the bucket boundaries:
   381  			// For 0--1, evenly spaced and not too many;
   382  			// For 1--2, roughly powers of sqrt(sqrt(2));
   383  			// For 2--6, roughly powers of sqrt(2);
   384  			// We need coverage over 1, but do not want too many buckets.
   385  			Buckets:        []float64{0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.7, 2, 2.8, 4, 6},
   386  			StabilityLevel: compbasemetrics.ALPHA,
   387  		},
   388  		priorityLevel,
   389  	)
   390  	apiserverSeatDemandHighWatermarks = compbasemetrics.NewGaugeVec(
   391  		&compbasemetrics.GaugeOpts{
   392  			Namespace:      namespace,
   393  			Subsystem:      subsystem,
   394  			Name:           "demand_seats_high_watermark",
   395  			Help:           "High watermark, over last adjustment period, of demand_seats",
   396  			StabilityLevel: compbasemetrics.ALPHA,
   397  		},
   398  		[]string{priorityLevel},
   399  	)
   400  	apiserverSeatDemandAverages = compbasemetrics.NewGaugeVec(
   401  		&compbasemetrics.GaugeOpts{
   402  			Namespace:      namespace,
   403  			Subsystem:      subsystem,
   404  			Name:           "demand_seats_average",
   405  			Help:           "Time-weighted average, over last adjustment period, of demand_seats",
   406  			StabilityLevel: compbasemetrics.ALPHA,
   407  		},
   408  		[]string{priorityLevel},
   409  	)
   410  	apiserverSeatDemandStandardDeviations = compbasemetrics.NewGaugeVec(
   411  		&compbasemetrics.GaugeOpts{
   412  			Namespace:      namespace,
   413  			Subsystem:      subsystem,
   414  			Name:           "demand_seats_stdev",
   415  			Help:           "Time-weighted standard deviation, over last adjustment period, of demand_seats",
   416  			StabilityLevel: compbasemetrics.ALPHA,
   417  		},
   418  		[]string{priorityLevel},
   419  	)
   420  	apiserverSeatDemandSmootheds = compbasemetrics.NewGaugeVec(
   421  		&compbasemetrics.GaugeOpts{
   422  			Namespace:      namespace,
   423  			Subsystem:      subsystem,
   424  			Name:           "demand_seats_smoothed",
   425  			Help:           "Smoothed seat demands",
   426  			StabilityLevel: compbasemetrics.ALPHA,
   427  		},
   428  		[]string{priorityLevel},
   429  	)
   430  	apiserverSeatDemandTargets = compbasemetrics.NewGaugeVec(
   431  		&compbasemetrics.GaugeOpts{
   432  			Namespace:      namespace,
   433  			Subsystem:      subsystem,
   434  			Name:           "target_seats",
   435  			Help:           "Seat allocation targets",
   436  			StabilityLevel: compbasemetrics.ALPHA,
   437  		},
   438  		[]string{priorityLevel},
   439  	)
   440  	apiserverFairFracs = compbasemetrics.NewGauge(
   441  		&compbasemetrics.GaugeOpts{
   442  			Namespace:      namespace,
   443  			Subsystem:      subsystem,
   444  			Name:           "seat_fair_frac",
   445  			Help:           "Fair fraction of server's concurrency to allocate to each priority level that can use it",
   446  			StabilityLevel: compbasemetrics.ALPHA,
   447  		})
   448  	apiserverCurrentConcurrencyLimits = compbasemetrics.NewGaugeVec(
   449  		&compbasemetrics.GaugeOpts{
   450  			Namespace:      namespace,
   451  			Subsystem:      subsystem,
   452  			Name:           "current_limit_seats",
   453  			Help:           "current derived number of execution seats available to each priority level",
   454  			StabilityLevel: compbasemetrics.ALPHA,
   455  		},
   456  		[]string{priorityLevel},
   457  	)
   458  
   459  	metrics = Registerables{
   460  		apiserverRejectedRequestsTotal,
   461  		apiserverDispatchedRequestsTotal,
   462  		apiserverCurrentR,
   463  		apiserverDispatchR,
   464  		apiserverLatestS,
   465  		apiserverNextSBounds,
   466  		apiserverNextDiscountedSBounds,
   467  		apiserverCurrentInqueueRequests,
   468  		apiserverCurrentInqueueSeats,
   469  		apiserverRequestQueueLength,
   470  		apiserverRequestConcurrencyLimit,
   471  		apiserverRequestConcurrencyInUse,
   472  		apiserverCurrentExecutingSeats,
   473  		apiserverCurrentExecutingRequests,
   474  		apiserverRequestWaitingSeconds,
   475  		apiserverRequestExecutionSeconds,
   476  		watchCountSamples,
   477  		apiserverEpochAdvances,
   478  		apiserverWorkEstimatedSeats,
   479  		apiserverDispatchWithNoAccommodation,
   480  		apiserverNominalConcurrencyLimits,
   481  		apiserverMinimumConcurrencyLimits,
   482  		apiserverMaximumConcurrencyLimits,
   483  		apiserverSeatDemandHighWatermarks,
   484  		apiserverSeatDemandAverages,
   485  		apiserverSeatDemandStandardDeviations,
   486  		apiserverSeatDemandSmootheds,
   487  		apiserverSeatDemandTargets,
   488  		apiserverFairFracs,
   489  		apiserverCurrentConcurrencyLimits,
   490  	}.
   491  		Append(PriorityLevelExecutionSeatsGaugeVec.metrics()...).
   492  		Append(PriorityLevelConcurrencyGaugeVec.metrics()...).
   493  		Append(readWriteConcurrencyGaugeVec.metrics()...).
   494  		Append(ApiserverSeatDemands.metrics()...)
   495  )
   496  
   497  type indexOnce struct {
   498  	labelValues []string
   499  	once        sync.Once
   500  	gauge       RatioedGauge
   501  }
   502  
   503  func (io *indexOnce) getGauge() RatioedGauge {
   504  	io.once.Do(func() {
   505  		io.gauge = readWriteConcurrencyGaugeVec.NewForLabelValuesSafe(0, 1, io.labelValues)
   506  	})
   507  	return io.gauge
   508  }
   509  
   510  var waitingReadonly = indexOnce{labelValues: []string{LabelValueWaiting, epmetrics.ReadOnlyKind}}
   511  var executingReadonly = indexOnce{labelValues: []string{LabelValueExecuting, epmetrics.ReadOnlyKind}}
   512  var waitingMutating = indexOnce{labelValues: []string{LabelValueWaiting, epmetrics.MutatingKind}}
   513  var executingMutating = indexOnce{labelValues: []string{LabelValueExecuting, epmetrics.MutatingKind}}
   514  
   515  // GetWaitingReadonlyConcurrency returns the gauge of number of readonly requests waiting / limit on those.
   516  var GetWaitingReadonlyConcurrency = waitingReadonly.getGauge
   517  
   518  // GetExecutingReadonlyConcurrency returns the gauge of number of executing readonly requests / limit on those.
   519  var GetExecutingReadonlyConcurrency = executingReadonly.getGauge
   520  
   521  // GetWaitingMutatingConcurrency returns the gauge of number of mutating requests waiting / limit on those.
   522  var GetWaitingMutatingConcurrency = waitingMutating.getGauge
   523  
   524  // GetExecutingMutatingConcurrency returns the gauge of number of executing mutating requests / limit on those.
   525  var GetExecutingMutatingConcurrency = executingMutating.getGauge
   526  
   527  // AddRequestsInQueues adds the given delta to the gauge of the # of requests in the queues of the specified flowSchema and priorityLevel
   528  func AddRequestsInQueues(ctx context.Context, priorityLevel, flowSchema string, delta int) {
   529  	apiserverCurrentInqueueRequests.WithLabelValues(priorityLevel, flowSchema).Add(float64(delta))
   530  }
   531  
   532  // AddSeatsInQueues adds the given delta to the gauge of the # of seats in the queues of the specified flowSchema and priorityLevel
   533  func AddSeatsInQueues(ctx context.Context, priorityLevel, flowSchema string, delta int) {
   534  	apiserverCurrentInqueueSeats.WithLabelValues(priorityLevel, flowSchema).Add(float64(delta))
   535  }
   536  
   537  // AddRequestsExecuting adds the given delta to the gauge of executing requests of the given flowSchema and priorityLevel
   538  func AddRequestsExecuting(ctx context.Context, priorityLevel, flowSchema string, delta int) {
   539  	apiserverCurrentExecutingRequests.WithLabelValues(priorityLevel, flowSchema).Add(float64(delta))
   540  }
   541  
   542  // SetCurrentR sets the current-R (virtualTime) gauge for the given priority level
   543  func SetCurrentR(priorityLevel string, r float64) {
   544  	apiserverCurrentR.WithLabelValues(priorityLevel).Set(r)
   545  }
   546  
   547  // SetLatestS sets the latest-S (virtual time of dispatched request) gauge for the given priority level
   548  func SetDispatchMetrics(priorityLevel string, r, s, sMin, sMax, discountedSMin, discountedSMax float64) {
   549  	apiserverDispatchR.WithLabelValues(priorityLevel).Set(r)
   550  	apiserverLatestS.WithLabelValues(priorityLevel).Set(s)
   551  	apiserverNextSBounds.WithLabelValues(priorityLevel, "min").Set(sMin)
   552  	apiserverNextSBounds.WithLabelValues(priorityLevel, "max").Set(sMax)
   553  	apiserverNextDiscountedSBounds.WithLabelValues(priorityLevel, "min").Set(discountedSMin)
   554  	apiserverNextDiscountedSBounds.WithLabelValues(priorityLevel, "max").Set(discountedSMax)
   555  }
   556  
   557  // AddSeatConcurrencyInUse adds the given delta to the gauge of seats in use by
   558  // the currently executing requests of the given flowSchema and priorityLevel
   559  func AddSeatConcurrencyInUse(priorityLevel, flowSchema string, delta int) {
   560  	apiserverCurrentExecutingSeats.WithLabelValues(priorityLevel, flowSchema).Add(float64(delta))
   561  	apiserverRequestConcurrencyInUse.WithLabelValues(priorityLevel, flowSchema).Add(float64(delta))
   562  }
   563  
   564  // AddReject increments the # of rejected requests for flow control
   565  func AddReject(ctx context.Context, priorityLevel, flowSchema, reason string) {
   566  	apiserverRejectedRequestsTotal.WithContext(ctx).WithLabelValues(priorityLevel, flowSchema, reason).Add(1)
   567  }
   568  
   569  // AddDispatch increments the # of dispatched requests for flow control
   570  func AddDispatch(ctx context.Context, priorityLevel, flowSchema string) {
   571  	apiserverDispatchedRequestsTotal.WithContext(ctx).WithLabelValues(priorityLevel, flowSchema).Add(1)
   572  }
   573  
   574  // ObserveQueueLength observes the queue length for flow control
   575  func ObserveQueueLength(ctx context.Context, priorityLevel, flowSchema string, length int) {
   576  	apiserverRequestQueueLength.WithContext(ctx).WithLabelValues(priorityLevel, flowSchema).Observe(float64(length))
   577  }
   578  
   579  // ObserveWaitingDuration observes the queue length for flow control
   580  func ObserveWaitingDuration(ctx context.Context, priorityLevel, flowSchema, execute string, waitTime time.Duration) {
   581  	apiserverRequestWaitingSeconds.WithContext(ctx).WithLabelValues(priorityLevel, flowSchema, execute).Observe(waitTime.Seconds())
   582  }
   583  
   584  // ObserveExecutionDuration observes the execution duration for flow control
   585  func ObserveExecutionDuration(ctx context.Context, priorityLevel, flowSchema string, executionTime time.Duration) {
   586  	reqType := "regular"
   587  	if requestInfo, ok := apirequest.RequestInfoFrom(ctx); ok && requestInfo.Verb == "watch" {
   588  		reqType = requestInfo.Verb
   589  	}
   590  	apiserverRequestExecutionSeconds.WithContext(ctx).WithLabelValues(priorityLevel, flowSchema, reqType).Observe(executionTime.Seconds())
   591  }
   592  
   593  // ObserveWatchCount notes a sampling of a watch count
   594  func ObserveWatchCount(ctx context.Context, priorityLevel, flowSchema string, count int) {
   595  	watchCountSamples.WithLabelValues(priorityLevel, flowSchema).Observe(float64(count))
   596  }
   597  
   598  // AddEpochAdvance notes an advance of the progress meter baseline for a given priority level
   599  func AddEpochAdvance(ctx context.Context, priorityLevel string, success bool) {
   600  	apiserverEpochAdvances.WithContext(ctx).WithLabelValues(priorityLevel, strconv.FormatBool(success)).Inc()
   601  }
   602  
   603  // ObserveWorkEstimatedSeats notes a sampling of estimated seats associated with a request
   604  func ObserveWorkEstimatedSeats(priorityLevel, flowSchema string, seats int) {
   605  	apiserverWorkEstimatedSeats.WithLabelValues(priorityLevel, flowSchema).Observe(float64(seats))
   606  }
   607  
   608  // AddDispatchWithNoAccommodation keeps track of number of times dispatch attempt results
   609  // in a non accommodation due to lack of available seats.
   610  func AddDispatchWithNoAccommodation(priorityLevel, flowSchema string) {
   611  	apiserverDispatchWithNoAccommodation.WithLabelValues(priorityLevel, flowSchema).Inc()
   612  }
   613  
   614  func SetPriorityLevelConfiguration(priorityLevel string, nominalCL, minCL, maxCL int) {
   615  	apiserverRequestConcurrencyLimit.WithLabelValues(priorityLevel).Set(float64(nominalCL))
   616  	apiserverNominalConcurrencyLimits.WithLabelValues(priorityLevel).Set(float64(nominalCL))
   617  	apiserverMinimumConcurrencyLimits.WithLabelValues(priorityLevel).Set(float64(minCL))
   618  	apiserverMaximumConcurrencyLimits.WithLabelValues(priorityLevel).Set(float64(maxCL))
   619  }
   620  
   621  func NotePriorityLevelConcurrencyAdjustment(priorityLevel string, seatDemandHWM, seatDemandAvg, seatDemandStdev, seatDemandSmoothed, seatDemandTarget float64, currentCL int) {
   622  	apiserverSeatDemandHighWatermarks.WithLabelValues(priorityLevel).Set(seatDemandHWM)
   623  	apiserverSeatDemandAverages.WithLabelValues(priorityLevel).Set(seatDemandAvg)
   624  	apiserverSeatDemandStandardDeviations.WithLabelValues(priorityLevel).Set(seatDemandStdev)
   625  	apiserverSeatDemandSmootheds.WithLabelValues(priorityLevel).Set(seatDemandSmoothed)
   626  	apiserverSeatDemandTargets.WithLabelValues(priorityLevel).Set(seatDemandTarget)
   627  	apiserverCurrentConcurrencyLimits.WithLabelValues(priorityLevel).Set(float64(currentCL))
   628  }
   629  
   630  func SetFairFrac(fairFrac float64) {
   631  	apiserverFairFracs.Set(fairFrac)
   632  }