k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/request/width.go (about)

     1  /*
     2  Copyright 2021 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 request
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"time"
    23  
    24  	apirequest "k8s.io/apiserver/pkg/endpoints/request"
    25  	"k8s.io/apiserver/pkg/features"
    26  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    27  
    28  	"k8s.io/klog/v2"
    29  )
    30  
    31  // WorkEstimate carries three of the four parameters that determine the work in a request.
    32  // The fourth parameter is the duration of the initial phase of execution.
    33  type WorkEstimate struct {
    34  	// InitialSeats is the number of seats occupied while the server is
    35  	// executing this request.
    36  	InitialSeats uint64
    37  
    38  	// FinalSeats is the number of seats occupied at the end,
    39  	// during the AdditionalLatency.
    40  	FinalSeats uint64
    41  
    42  	// AdditionalLatency specifies the additional duration the seats allocated
    43  	// to this request must be reserved after the given request had finished.
    44  	// AdditionalLatency should not have any impact on the user experience, the
    45  	// caller must not experience this additional latency.
    46  	AdditionalLatency time.Duration
    47  }
    48  
    49  // MaxSeats returns the maximum number of seats the request occupies over the
    50  // phases of being served.
    51  func (we *WorkEstimate) MaxSeats() int {
    52  	if we.InitialSeats >= we.FinalSeats {
    53  		return int(we.InitialSeats)
    54  	}
    55  
    56  	return int(we.FinalSeats)
    57  }
    58  
    59  // objectCountGetterFunc represents a function that gets the total
    60  // number of objects for a given resource.
    61  type objectCountGetterFunc func(string) (int64, error)
    62  
    63  // watchCountGetterFunc represents a function that gets the total
    64  // number of watchers potentially interested in a given request.
    65  type watchCountGetterFunc func(*apirequest.RequestInfo) int
    66  
    67  // MaxSeatsFunc represents a function that returns the maximum seats
    68  // allowed for the work estimator for a given priority level.
    69  type maxSeatsFunc func(priorityLevelName string) uint64
    70  
    71  // NewWorkEstimator estimates the work that will be done by a given request,
    72  // if no WorkEstimatorFunc matches the given request then the default
    73  // work estimate of 1 seat is allocated to the request.
    74  func NewWorkEstimator(objectCountFn objectCountGetterFunc, watchCountFn watchCountGetterFunc, config *WorkEstimatorConfig, maxSeatsFn maxSeatsFunc) WorkEstimatorFunc {
    75  	estimator := &workEstimator{
    76  		minimumSeats:          config.MinimumSeats,
    77  		maximumSeatsLimit:     config.MaximumSeatsLimit,
    78  		listWorkEstimator:     newListWorkEstimator(objectCountFn, config, maxSeatsFn),
    79  		mutatingWorkEstimator: newMutatingWorkEstimator(watchCountFn, config, maxSeatsFn),
    80  	}
    81  	return estimator.estimate
    82  }
    83  
    84  // WorkEstimatorFunc returns the estimated work of a given request.
    85  // This function will be used by the Priority & Fairness filter to
    86  // estimate the work of of incoming requests.
    87  type WorkEstimatorFunc func(request *http.Request, flowSchemaName, priorityLevelName string) WorkEstimate
    88  
    89  func (e WorkEstimatorFunc) EstimateWork(r *http.Request, flowSchemaName, priorityLevelName string) WorkEstimate {
    90  	return e(r, flowSchemaName, priorityLevelName)
    91  }
    92  
    93  type workEstimator struct {
    94  	// the minimum number of seats a request must occupy
    95  	minimumSeats uint64
    96  	// the default maximum number of seats a request can occupy
    97  	maximumSeatsLimit uint64
    98  	// listWorkEstimator estimates work for list request(s)
    99  	listWorkEstimator WorkEstimatorFunc
   100  	// mutatingWorkEstimator calculates the width of mutating request(s)
   101  	mutatingWorkEstimator WorkEstimatorFunc
   102  }
   103  
   104  func (e *workEstimator) estimate(r *http.Request, flowSchemaName, priorityLevelName string) WorkEstimate {
   105  	requestInfo, ok := apirequest.RequestInfoFrom(r.Context())
   106  	if !ok {
   107  		klog.ErrorS(fmt.Errorf("no RequestInfo found in context"), "Failed to estimate work for the request", "URI", r.RequestURI)
   108  		// no RequestInfo should never happen, but to be on the safe side let's return maximumSeats
   109  		return WorkEstimate{InitialSeats: e.maximumSeatsLimit}
   110  	}
   111  
   112  	switch requestInfo.Verb {
   113  	case "list":
   114  		return e.listWorkEstimator.EstimateWork(r, flowSchemaName, priorityLevelName)
   115  	case "watch":
   116  		// WATCH supports `SendInitialEvents` option, which effectively means
   117  		// that is starts with sending of the contents of a corresponding LIST call.
   118  		// From that perspective, given that the watch only consumes APF seats
   119  		// during its initialization (sending init events), its cost should then
   120  		// be computed the same way as for a regular list.
   121  		if utilfeature.DefaultFeatureGate.Enabled(features.WatchList) {
   122  			return e.listWorkEstimator.EstimateWork(r, flowSchemaName, priorityLevelName)
   123  		}
   124  	case "create", "update", "patch", "delete":
   125  		return e.mutatingWorkEstimator.EstimateWork(r, flowSchemaName, priorityLevelName)
   126  	}
   127  
   128  	return WorkEstimate{InitialSeats: e.minimumSeats}
   129  }