google.golang.org/grpc@v1.62.1/orca/server_metrics.go (about) 1 /* 2 * 3 * Copyright 2023 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package orca 20 21 import ( 22 "sync/atomic" 23 24 v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3" 25 ) 26 27 // ServerMetrics is the data returned from a server to a client to describe the 28 // current state of the server and/or the cost of a request when used per-call. 29 type ServerMetrics struct { 30 CPUUtilization float64 // CPU utilization: [0, inf); unset=-1 31 MemUtilization float64 // Memory utilization: [0, 1.0]; unset=-1 32 AppUtilization float64 // Application utilization: [0, inf); unset=-1 33 QPS float64 // queries per second: [0, inf); unset=-1 34 EPS float64 // errors per second: [0, inf); unset=-1 35 36 // The following maps must never be nil. 37 38 Utilization map[string]float64 // Custom fields: [0, 1.0] 39 RequestCost map[string]float64 // Custom fields: [0, inf); not sent OOB 40 NamedMetrics map[string]float64 // Custom fields: [0, inf); not sent OOB 41 } 42 43 // toLoadReportProto dumps sm as an OrcaLoadReport proto. 44 func (sm *ServerMetrics) toLoadReportProto() *v3orcapb.OrcaLoadReport { 45 ret := &v3orcapb.OrcaLoadReport{ 46 Utilization: sm.Utilization, 47 RequestCost: sm.RequestCost, 48 NamedMetrics: sm.NamedMetrics, 49 } 50 if sm.CPUUtilization != -1 { 51 ret.CpuUtilization = sm.CPUUtilization 52 } 53 if sm.MemUtilization != -1 { 54 ret.MemUtilization = sm.MemUtilization 55 } 56 if sm.AppUtilization != -1 { 57 ret.ApplicationUtilization = sm.AppUtilization 58 } 59 if sm.QPS != -1 { 60 ret.RpsFractional = sm.QPS 61 } 62 if sm.EPS != -1 { 63 ret.Eps = sm.EPS 64 } 65 return ret 66 } 67 68 // merge merges o into sm, overwriting any values present in both. 69 func (sm *ServerMetrics) merge(o *ServerMetrics) { 70 mergeMap(sm.Utilization, o.Utilization) 71 mergeMap(sm.RequestCost, o.RequestCost) 72 mergeMap(sm.NamedMetrics, o.NamedMetrics) 73 if o.CPUUtilization != -1 { 74 sm.CPUUtilization = o.CPUUtilization 75 } 76 if o.MemUtilization != -1 { 77 sm.MemUtilization = o.MemUtilization 78 } 79 if o.AppUtilization != -1 { 80 sm.AppUtilization = o.AppUtilization 81 } 82 if o.QPS != -1 { 83 sm.QPS = o.QPS 84 } 85 if o.EPS != -1 { 86 sm.EPS = o.EPS 87 } 88 } 89 90 func mergeMap(a, b map[string]float64) { 91 for k, v := range b { 92 a[k] = v 93 } 94 } 95 96 // ServerMetricsRecorder allows for recording and providing out of band server 97 // metrics. 98 type ServerMetricsRecorder interface { 99 ServerMetricsProvider 100 101 // SetCPUUtilization sets the CPU utilization server metric. Must be 102 // greater than zero. 103 SetCPUUtilization(float64) 104 // DeleteCPUUtilization deletes the CPU utilization server metric to 105 // prevent it from being sent. 106 DeleteCPUUtilization() 107 108 // SetMemoryUtilization sets the memory utilization server metric. Must be 109 // in the range [0, 1]. 110 SetMemoryUtilization(float64) 111 // DeleteMemoryUtilization deletes the memory utiliztion server metric to 112 // prevent it from being sent. 113 DeleteMemoryUtilization() 114 115 // SetApplicationUtilization sets the application utilization server 116 // metric. Must be greater than zero. 117 SetApplicationUtilization(float64) 118 // DeleteApplicationUtilization deletes the application utilization server 119 // metric to prevent it from being sent. 120 DeleteApplicationUtilization() 121 122 // SetQPS sets the Queries Per Second server metric. Must be greater than 123 // zero. 124 SetQPS(float64) 125 // DeleteQPS deletes the Queries Per Second server metric to prevent it 126 // from being sent. 127 DeleteQPS() 128 129 // SetEPS sets the Errors Per Second server metric. Must be greater than 130 // zero. 131 SetEPS(float64) 132 // DeleteEPS deletes the Errors Per Second server metric to prevent it from 133 // being sent. 134 DeleteEPS() 135 136 // SetNamedUtilization sets the named utilization server metric for the 137 // name provided. val must be in the range [0, 1]. 138 SetNamedUtilization(name string, val float64) 139 // DeleteNamedUtilization deletes the named utilization server metric for 140 // the name provided to prevent it from being sent. 141 DeleteNamedUtilization(name string) 142 } 143 144 type serverMetricsRecorder struct { 145 state atomic.Pointer[ServerMetrics] // the current metrics 146 } 147 148 // NewServerMetricsRecorder returns an in-memory store for ServerMetrics and 149 // allows for safe setting and retrieving of ServerMetrics. Also implements 150 // ServerMetricsProvider for use with NewService. 151 func NewServerMetricsRecorder() ServerMetricsRecorder { 152 return newServerMetricsRecorder() 153 } 154 155 func newServerMetricsRecorder() *serverMetricsRecorder { 156 s := new(serverMetricsRecorder) 157 s.state.Store(&ServerMetrics{ 158 CPUUtilization: -1, 159 MemUtilization: -1, 160 AppUtilization: -1, 161 QPS: -1, 162 EPS: -1, 163 Utilization: make(map[string]float64), 164 RequestCost: make(map[string]float64), 165 NamedMetrics: make(map[string]float64), 166 }) 167 return s 168 } 169 170 // ServerMetrics returns a copy of the current ServerMetrics. 171 func (s *serverMetricsRecorder) ServerMetrics() *ServerMetrics { 172 return copyServerMetrics(s.state.Load()) 173 } 174 175 func copyMap(m map[string]float64) map[string]float64 { 176 ret := make(map[string]float64, len(m)) 177 for k, v := range m { 178 ret[k] = v 179 } 180 return ret 181 } 182 183 func copyServerMetrics(sm *ServerMetrics) *ServerMetrics { 184 return &ServerMetrics{ 185 CPUUtilization: sm.CPUUtilization, 186 MemUtilization: sm.MemUtilization, 187 AppUtilization: sm.AppUtilization, 188 QPS: sm.QPS, 189 EPS: sm.EPS, 190 Utilization: copyMap(sm.Utilization), 191 RequestCost: copyMap(sm.RequestCost), 192 NamedMetrics: copyMap(sm.NamedMetrics), 193 } 194 } 195 196 // SetCPUUtilization records a measurement for the CPU utilization metric. 197 func (s *serverMetricsRecorder) SetCPUUtilization(val float64) { 198 if val < 0 { 199 if logger.V(2) { 200 logger.Infof("Ignoring CPU Utilization value out of range: %v", val) 201 } 202 return 203 } 204 smCopy := copyServerMetrics(s.state.Load()) 205 smCopy.CPUUtilization = val 206 s.state.Store(smCopy) 207 } 208 209 // DeleteCPUUtilization deletes the relevant server metric to prevent it from 210 // being sent. 211 func (s *serverMetricsRecorder) DeleteCPUUtilization() { 212 smCopy := copyServerMetrics(s.state.Load()) 213 smCopy.CPUUtilization = -1 214 s.state.Store(smCopy) 215 } 216 217 // SetMemoryUtilization records a measurement for the memory utilization metric. 218 func (s *serverMetricsRecorder) SetMemoryUtilization(val float64) { 219 if val < 0 || val > 1 { 220 if logger.V(2) { 221 logger.Infof("Ignoring Memory Utilization value out of range: %v", val) 222 } 223 return 224 } 225 smCopy := copyServerMetrics(s.state.Load()) 226 smCopy.MemUtilization = val 227 s.state.Store(smCopy) 228 } 229 230 // DeleteMemoryUtilization deletes the relevant server metric to prevent it 231 // from being sent. 232 func (s *serverMetricsRecorder) DeleteMemoryUtilization() { 233 smCopy := copyServerMetrics(s.state.Load()) 234 smCopy.MemUtilization = -1 235 s.state.Store(smCopy) 236 } 237 238 // SetApplicationUtilization records a measurement for a generic utilization 239 // metric. 240 func (s *serverMetricsRecorder) SetApplicationUtilization(val float64) { 241 if val < 0 { 242 if logger.V(2) { 243 logger.Infof("Ignoring Application Utilization value out of range: %v", val) 244 } 245 return 246 } 247 smCopy := copyServerMetrics(s.state.Load()) 248 smCopy.AppUtilization = val 249 s.state.Store(smCopy) 250 } 251 252 // DeleteApplicationUtilization deletes the relevant server metric to prevent 253 // it from being sent. 254 func (s *serverMetricsRecorder) DeleteApplicationUtilization() { 255 smCopy := copyServerMetrics(s.state.Load()) 256 smCopy.AppUtilization = -1 257 s.state.Store(smCopy) 258 } 259 260 // SetQPS records a measurement for the QPS metric. 261 func (s *serverMetricsRecorder) SetQPS(val float64) { 262 if val < 0 { 263 if logger.V(2) { 264 logger.Infof("Ignoring QPS value out of range: %v", val) 265 } 266 return 267 } 268 smCopy := copyServerMetrics(s.state.Load()) 269 smCopy.QPS = val 270 s.state.Store(smCopy) 271 } 272 273 // DeleteQPS deletes the relevant server metric to prevent it from being sent. 274 func (s *serverMetricsRecorder) DeleteQPS() { 275 smCopy := copyServerMetrics(s.state.Load()) 276 smCopy.QPS = -1 277 s.state.Store(smCopy) 278 } 279 280 // SetEPS records a measurement for the EPS metric. 281 func (s *serverMetricsRecorder) SetEPS(val float64) { 282 if val < 0 { 283 if logger.V(2) { 284 logger.Infof("Ignoring EPS value out of range: %v", val) 285 } 286 return 287 } 288 smCopy := copyServerMetrics(s.state.Load()) 289 smCopy.EPS = val 290 s.state.Store(smCopy) 291 } 292 293 // DeleteEPS deletes the relevant server metric to prevent it from being sent. 294 func (s *serverMetricsRecorder) DeleteEPS() { 295 smCopy := copyServerMetrics(s.state.Load()) 296 smCopy.EPS = -1 297 s.state.Store(smCopy) 298 } 299 300 // SetNamedUtilization records a measurement for a utilization metric uniquely 301 // identifiable by name. 302 func (s *serverMetricsRecorder) SetNamedUtilization(name string, val float64) { 303 if val < 0 || val > 1 { 304 if logger.V(2) { 305 logger.Infof("Ignoring Named Utilization value out of range: %v", val) 306 } 307 return 308 } 309 smCopy := copyServerMetrics(s.state.Load()) 310 smCopy.Utilization[name] = val 311 s.state.Store(smCopy) 312 } 313 314 // DeleteNamedUtilization deletes any previously recorded measurement for a 315 // utilization metric uniquely identifiable by name. 316 func (s *serverMetricsRecorder) DeleteNamedUtilization(name string) { 317 smCopy := copyServerMetrics(s.state.Load()) 318 delete(smCopy.Utilization, name) 319 s.state.Store(smCopy) 320 } 321 322 // SetRequestCost records a measurement for a utilization metric uniquely 323 // identifiable by name. 324 func (s *serverMetricsRecorder) SetRequestCost(name string, val float64) { 325 smCopy := copyServerMetrics(s.state.Load()) 326 smCopy.RequestCost[name] = val 327 s.state.Store(smCopy) 328 } 329 330 // DeleteRequestCost deletes any previously recorded measurement for a 331 // utilization metric uniquely identifiable by name. 332 func (s *serverMetricsRecorder) DeleteRequestCost(name string) { 333 smCopy := copyServerMetrics(s.state.Load()) 334 delete(smCopy.RequestCost, name) 335 s.state.Store(smCopy) 336 } 337 338 // SetNamedMetric records a measurement for a utilization metric uniquely 339 // identifiable by name. 340 func (s *serverMetricsRecorder) SetNamedMetric(name string, val float64) { 341 smCopy := copyServerMetrics(s.state.Load()) 342 smCopy.NamedMetrics[name] = val 343 s.state.Store(smCopy) 344 } 345 346 // DeleteNamedMetric deletes any previously recorded measurement for a 347 // utilization metric uniquely identifiable by name. 348 func (s *serverMetricsRecorder) DeleteNamedMetric(name string) { 349 smCopy := copyServerMetrics(s.state.Load()) 350 delete(smCopy.NamedMetrics, name) 351 s.state.Store(smCopy) 352 }