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  }