github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/bench/tools/aisloader/stats/statsdplus.go (about)

     1  // Package stats provides various structs for collecting stats
     2  /*
     3   * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package stats
     6  
     7  import (
     8  	"time"
     9  
    10  	"github.com/NVIDIA/aistore/stats/statsd"
    11  )
    12  
    13  const (
    14  	get              = "get"
    15  	put              = "put"
    16  	getConfig        = "getconfig"
    17  	throughput       = "throughput"
    18  	latency          = "latency"
    19  	latencyProxy     = "latency.proxy"
    20  	latencyProxyConn = "latency.proxyconn"
    21  	minLatency       = "minlatency"
    22  	maxLatency       = "maxlatency"
    23  	pending          = "pending"
    24  	count            = "count"
    25  )
    26  
    27  type (
    28  	BaseMetricAgg struct {
    29  		name  string
    30  		start time.Time // time current stats started
    31  		cnt   int64     // total # of requests
    32  	}
    33  	MetricAgg struct {
    34  		MetricLatAgg
    35  		bytes   int64 // total bytes by all requests
    36  		errs    int64 // number of failed requests
    37  		pending int64
    38  	}
    39  	MetricLatAgg struct {
    40  		BaseMetricAgg
    41  		latency time.Duration // Accumulated request latency
    42  
    43  		// self maintained fields
    44  		minLatency time.Duration
    45  		maxLatency time.Duration
    46  	}
    47  	MetricConfigAgg struct {
    48  		BaseMetricAgg
    49  		latency             time.Duration
    50  		minLatency          time.Duration
    51  		maxLatency          time.Duration
    52  		proxyLatency        time.Duration
    53  		minProxyLatency     time.Duration
    54  		maxProxyLatency     time.Duration
    55  		proxyConnLatency    time.Duration
    56  		minProxyConnLatency time.Duration
    57  		maxProxyConnLatency time.Duration
    58  	}
    59  	MetricLatsAgg struct {
    60  		metrics map[string]*MetricLatAgg
    61  	}
    62  	Metrics struct {
    63  		Put    MetricAgg
    64  		Get    MetricAgg
    65  		Config MetricConfigAgg
    66  		PutLat MetricLatsAgg
    67  		GetLat MetricLatsAgg
    68  	}
    69  )
    70  
    71  func NewStatsdMetrics(start time.Time) Metrics {
    72  	m := Metrics{}
    73  	m.Get.start = start
    74  	m.Put.start = start
    75  	m.Config.start = start
    76  	return m
    77  }
    78  
    79  func (ma *MetricAgg) Add(size int64, lat time.Duration) {
    80  	ma.cnt++
    81  	ma.latency += lat
    82  	ma.bytes += size
    83  	ma.minLatency = min(ma.minLatency, lat)
    84  	ma.maxLatency = max(ma.maxLatency, lat)
    85  }
    86  
    87  func (ma *MetricAgg) AddPending(pending int64) {
    88  	ma.pending += pending
    89  }
    90  
    91  func (ma *MetricAgg) AddErr() {
    92  	ma.errs++
    93  }
    94  
    95  func (ma *MetricAgg) AvgLatency() float64 {
    96  	if ma.cnt == 0 {
    97  		return 0
    98  	}
    99  	return float64(ma.latency/time.Millisecond) / float64(ma.cnt)
   100  }
   101  
   102  func (ma *MetricAgg) Throughput(end time.Time) int64 {
   103  	if ma.cnt == 0 {
   104  		return 0
   105  	}
   106  	if end == ma.start {
   107  		return 0
   108  	}
   109  
   110  	return int64(float64(ma.bytes) / end.Sub(ma.start).Seconds())
   111  }
   112  
   113  func (mgs *MetricLatsAgg) Add(name string, lat time.Duration) {
   114  	if mgs.metrics == nil {
   115  		mgs.metrics = make(map[string]*MetricLatAgg)
   116  	}
   117  
   118  	if val, ok := mgs.metrics[name]; !ok {
   119  		mgs.metrics[name] = &MetricLatAgg{
   120  			BaseMetricAgg: BaseMetricAgg{
   121  				start: time.Now(),
   122  				cnt:   1,
   123  				name:  name,
   124  			},
   125  
   126  			latency:    lat,
   127  			minLatency: lat,
   128  			maxLatency: lat,
   129  		}
   130  	} else {
   131  		val.cnt++
   132  		val.latency += lat
   133  		val.maxLatency = max(val.maxLatency, lat)
   134  		val.minLatency = min(val.minLatency, lat)
   135  	}
   136  }
   137  
   138  func (mcg *MetricConfigAgg) Add(lat, _, _ time.Duration) {
   139  	mcg.cnt++
   140  
   141  	mcg.latency += lat
   142  	mcg.minLatency = min(mcg.minLatency, lat)
   143  	mcg.maxLatency = max(mcg.maxLatency, lat)
   144  
   145  	mcg.proxyLatency += lat
   146  	mcg.minProxyLatency = min(mcg.minProxyLatency, lat)
   147  	mcg.maxProxyLatency = max(mcg.maxProxyLatency, lat)
   148  
   149  	mcg.proxyConnLatency += lat
   150  	mcg.minProxyConnLatency = min(mcg.minProxyConnLatency, lat)
   151  	mcg.maxProxyConnLatency = max(mcg.maxProxyConnLatency, lat)
   152  }
   153  
   154  func (ma *MetricAgg) Send(c *statsd.Client, mType string, general []statsd.Metric, genAggCnt int64) {
   155  	endTime := time.Now()
   156  	c.Send(mType, 1, statsd.Metric{
   157  		Type:  statsd.Counter,
   158  		Name:  count,
   159  		Value: ma.cnt,
   160  	})
   161  	// don't send anything when cnt == 0 -> no data aggregated
   162  	if ma.cnt > 0 {
   163  		c.Send(mType, ma.cnt, statsd.Metric{
   164  			Type:  statsd.Gauge,
   165  			Name:  pending,
   166  			Value: ma.pending / ma.cnt,
   167  		})
   168  		c.Send(mType, ma.cnt,
   169  			statsd.Metric{
   170  				Type:  statsd.Timer,
   171  				Name:  latency,
   172  				Value: ma.AvgLatency(),
   173  			},
   174  			statsd.Metric{
   175  				Type:  statsd.Timer,
   176  				Name:  minLatency,
   177  				Value: float64(ma.minLatency / time.Millisecond),
   178  			},
   179  			statsd.Metric{
   180  				Type:  statsd.Timer,
   181  				Name:  maxLatency,
   182  				Value: float64(ma.maxLatency / time.Millisecond),
   183  			},
   184  		)
   185  		c.Send(mType, ma.cnt, statsd.Metric{
   186  			Type:  statsd.Gauge,
   187  			Name:  throughput,
   188  			Value: ma.Throughput(endTime),
   189  		})
   190  	}
   191  	if len(general) != 0 && genAggCnt > 0 {
   192  		c.Send(mType, genAggCnt, general...)
   193  	}
   194  }
   195  
   196  func (mcg *MetricConfigAgg) Send(c *statsd.Client) {
   197  	// don't send anything when cnt == 0 -> no data aggregated
   198  	if mcg.cnt == 0 {
   199  		return
   200  	}
   201  	c.Send(getConfig, 1,
   202  		statsd.Metric{
   203  			Type:  statsd.Counter,
   204  			Name:  count,
   205  			Value: mcg.cnt,
   206  		})
   207  	c.Send(getConfig, mcg.cnt,
   208  		statsd.Metric{
   209  			Type:  statsd.Timer,
   210  			Name:  latency,
   211  			Value: float64(mcg.latency/time.Millisecond) / float64(mcg.cnt),
   212  		},
   213  		statsd.Metric{
   214  			Type:  statsd.Timer,
   215  			Name:  latencyProxyConn,
   216  			Value: float64(mcg.proxyConnLatency/time.Millisecond) / float64(mcg.cnt),
   217  		},
   218  		statsd.Metric{
   219  			Type:  statsd.Timer,
   220  			Name:  latencyProxy,
   221  			Value: float64(mcg.proxyLatency/time.Millisecond) / float64(mcg.cnt),
   222  		},
   223  	)
   224  }
   225  
   226  func (m *Metrics) SendAll(c *statsd.Client) {
   227  	var (
   228  		aggCntGet, aggCntPut int64
   229  		generalMetricsGet    = make([]statsd.Metric, 0, len(m.GetLat.metrics))
   230  		generalMetricsPut    = make([]statsd.Metric, 0, len(m.PutLat.metrics))
   231  	)
   232  	for _, m := range m.GetLat.metrics {
   233  		generalMetricsGet = append(generalMetricsGet, statsd.Metric{
   234  			Type:  statsd.Timer,
   235  			Name:  m.name,
   236  			Value: float64(m.latency/time.Millisecond) / float64(m.cnt),
   237  		})
   238  		// m.cnt is the same for all aggregated metrics
   239  		aggCntGet = m.cnt
   240  	}
   241  	for _, m := range m.PutLat.metrics {
   242  		generalMetricsPut = append(generalMetricsPut, statsd.Metric{
   243  			Type:  statsd.Timer,
   244  			Name:  m.name,
   245  			Value: float64(m.latency/time.Millisecond) / float64(m.cnt),
   246  		})
   247  		// m.cnt is the same for all aggregated metrics
   248  		aggCntPut = m.cnt
   249  	}
   250  
   251  	m.Get.Send(c, get, generalMetricsGet, aggCntGet)
   252  	m.Put.Send(c, put, generalMetricsPut, aggCntPut)
   253  
   254  	m.Config.Send(c)
   255  }
   256  
   257  func ResetMetricsGauges(c *statsd.Client) {
   258  	c.Send(get, 1,
   259  		statsd.Metric{
   260  			Type:  statsd.Gauge,
   261  			Name:  throughput,
   262  			Value: 0,
   263  		}, statsd.Metric{
   264  			Type:  statsd.Gauge,
   265  			Name:  pending,
   266  			Value: 0,
   267  		})
   268  
   269  	c.Send(put, 1,
   270  		statsd.Metric{
   271  			Type:  statsd.Gauge,
   272  			Name:  throughput,
   273  			Value: 0,
   274  		}, statsd.Metric{
   275  			Type:  statsd.Gauge,
   276  			Name:  pending,
   277  			Value: 0,
   278  		})
   279  }