k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/fairqueuing/queueset/types.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 queueset 18 19 import ( 20 "context" 21 "time" 22 23 "k8s.io/apimachinery/pkg/util/sets" 24 genericrequest "k8s.io/apiserver/pkg/endpoints/request" 25 "k8s.io/apiserver/pkg/util/flowcontrol/debug" 26 fq "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing" 27 "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/promise" 28 fcrequest "k8s.io/apiserver/pkg/util/flowcontrol/request" 29 ) 30 31 // request is a temporary container for "requests" with additional 32 // tracking fields required for QueueSet functionality. 33 type request struct { 34 ctx context.Context 35 36 qs *queueSet 37 38 flowDistinguisher string 39 fsName string 40 41 // The relevant queue. Is nil if this request did not go through 42 // a queue. 43 queue *queue 44 45 // estimated amount of work of the request 46 workEstimate completedWorkEstimate 47 48 // decision gets set to a `requestDecision` indicating what to do 49 // with this request. It gets set exactly once, when the request 50 // is removed from its queue. The value will be decisionReject, 51 // decisionCancel, or decisionExecute. 52 // 53 // decision.Set is called with the queueSet locked. 54 // decision.Get is called without the queueSet locked. 55 decision promise.WriteOnce 56 57 // arrivalTime is the real time when the request entered this system 58 arrivalTime time.Time 59 60 // descr1 and descr2 are not used in any logic but they appear in 61 // log messages 62 descr1, descr2 interface{} 63 64 queueNoteFn fq.QueueNoteFn 65 66 // The preceding fields are filled in at creation and not modified since; 67 // the following fields may be modified later and must only be accessed while 68 // holding the queueSet's lock. 69 70 // Removes this request from its queue. If the request is not put into a 71 // a queue it will be nil. 72 removeFromQueueLocked removeFromFIFOFunc 73 74 // arrivalR is R(arrivalTime). R is, confusingly, also called "virtual time". 75 // This field is meaningful only while the request is waiting in the virtual world. 76 arrivalR fcrequest.SeatSeconds 77 78 // startTime is the real time when the request began executing 79 startTime time.Time 80 81 // Indicates whether client has called Request::Wait() 82 waitStarted bool 83 } 84 85 type completedWorkEstimate struct { 86 fcrequest.WorkEstimate 87 totalWork fcrequest.SeatSeconds // initial plus final work 88 finalWork fcrequest.SeatSeconds // only final work 89 } 90 91 // queue is a sequence of requests that have arrived but not yet finished 92 // execution in both the real and virtual worlds. 93 type queue struct { 94 // The requestsWaiting not yet executing in the real world are stored in a FIFO list. 95 requestsWaiting fifo 96 97 // nextDispatchR is the R progress meter reading at 98 // which the next request will be dispatched in the virtual world. 99 nextDispatchR fcrequest.SeatSeconds 100 101 // requestsExecuting is the set of requests executing in the real world. 102 requestsExecuting sets.Set[*request] 103 104 // index is the position of this queue among those in its queueSet. 105 index int 106 107 // seatsInUse is the total number of "seats" currently occupied 108 // by all the requests that are currently executing in this queue. 109 seatsInUse int 110 } 111 112 // queueSum tracks the sum of initial seats, max seats, and 113 // totalWork from all requests in a given queue 114 type queueSum struct { 115 // InitialSeatsSum is the sum of InitialSeats 116 // associated with all requests in a given queue. 117 InitialSeatsSum int 118 119 // MaxSeatsSum is the sum of MaxSeats 120 // associated with all requests in a given queue. 121 MaxSeatsSum int 122 123 // TotalWorkSum is the sum of totalWork of the waiting requests 124 TotalWorkSum fcrequest.SeatSeconds 125 } 126 127 func (req *request) totalWork() fcrequest.SeatSeconds { 128 return req.workEstimate.totalWork 129 } 130 131 func (qs *queueSet) completeWorkEstimate(we *fcrequest.WorkEstimate) completedWorkEstimate { 132 finalWork := qs.computeFinalWork(we) 133 return completedWorkEstimate{ 134 WorkEstimate: *we, 135 totalWork: qs.computeInitialWork(we) + finalWork, 136 finalWork: finalWork, 137 } 138 } 139 140 func (qs *queueSet) computeInitialWork(we *fcrequest.WorkEstimate) fcrequest.SeatSeconds { 141 return fcrequest.SeatsTimesDuration(float64(we.InitialSeats), qs.estimatedServiceDuration) 142 } 143 144 func (qs *queueSet) computeFinalWork(we *fcrequest.WorkEstimate) fcrequest.SeatSeconds { 145 return fcrequest.SeatsTimesDuration(float64(we.FinalSeats), we.AdditionalLatency) 146 } 147 148 func (q *queue) dumpLocked(includeDetails bool) debug.QueueDump { 149 waitingDigest := make([]debug.RequestDump, 0, q.requestsWaiting.Length()) 150 q.requestsWaiting.Walk(func(r *request) bool { 151 waitingDigest = append(waitingDigest, dumpRequest(includeDetails)(r)) 152 return true 153 }) 154 executingDigest := SetMapReduce(dumpRequest(includeDetails), append1[debug.RequestDump])(q.requestsExecuting) 155 156 sum := q.requestsWaiting.QueueSum() 157 queueSum := debug.QueueSum{ 158 InitialSeatsSum: sum.InitialSeatsSum, 159 MaxSeatsSum: sum.MaxSeatsSum, 160 TotalWorkSum: sum.TotalWorkSum.String(), 161 } 162 163 return debug.QueueDump{ 164 NextDispatchR: q.nextDispatchR.String(), 165 Requests: waitingDigest, 166 RequestsExecuting: executingDigest, 167 ExecutingRequests: q.requestsExecuting.Len(), 168 SeatsInUse: q.seatsInUse, 169 QueueSum: queueSum, 170 } 171 } 172 173 func dumpRequest(includeDetails bool) func(*request) debug.RequestDump { 174 return func(r *request) debug.RequestDump { 175 ans := debug.RequestDump{ 176 MatchedFlowSchema: r.fsName, 177 FlowDistinguisher: r.flowDistinguisher, 178 ArriveTime: r.arrivalTime, 179 StartTime: r.startTime, 180 WorkEstimate: r.workEstimate.WorkEstimate, 181 } 182 if includeDetails { 183 userInfo, _ := genericrequest.UserFrom(r.ctx) 184 ans.UserName = userInfo.GetName() 185 requestInfo, ok := genericrequest.RequestInfoFrom(r.ctx) 186 if ok { 187 ans.RequestInfo = *requestInfo 188 } 189 } 190 return ans 191 } 192 } 193 194 // SetMapReduce is map-reduce starting from a set type in the sets package. 195 func SetMapReduce[Elt comparable, Result, Accumulator any](mapFn func(Elt) Result, reduceFn func(Accumulator, Result) Accumulator) func(map[Elt]sets.Empty) Accumulator { 196 return func(set map[Elt]sets.Empty) Accumulator { 197 var ans Accumulator 198 for elt := range set { 199 ans = reduceFn(ans, mapFn(elt)) 200 } 201 return ans 202 } 203 } 204 205 // SliceMapReduce is map-reduce starting from a slice. 206 func SliceMapReduce[Elt, Result, Accumulator any](mapFn func(Elt) Result, reduceFn func(Accumulator, Result) Accumulator) func([]Elt) Accumulator { 207 return func(slice []Elt) Accumulator { 208 var ans Accumulator 209 for _, elt := range slice { 210 ans = reduceFn(ans, mapFn(elt)) 211 } 212 return ans 213 } 214 } 215 216 func or(x, y bool) bool { return x || y } 217 218 func append1[Elt any](slice []Elt, next Elt) []Elt { return append(slice, next) }