github.com/segmentio/kafka-go@v0.4.48-0.20240318174348-3f6244eb34fd/stats.go (about)

     1  package kafka
     2  
     3  import (
     4  	"sync/atomic"
     5  	"time"
     6  )
     7  
     8  // SummaryStats is a data structure that carries a summary of observed values.
     9  type SummaryStats struct {
    10  	Avg   int64 `metric:"avg" type:"gauge"`
    11  	Min   int64 `metric:"min" type:"gauge"`
    12  	Max   int64 `metric:"max" type:"gauge"`
    13  	Count int64 `metric:"count" type:"counter"`
    14  	Sum   int64 `metric:"sum" type:"counter"`
    15  }
    16  
    17  // DurationStats is a data structure that carries a summary of observed duration values.
    18  type DurationStats struct {
    19  	Avg   time.Duration `metric:"avg" type:"gauge"`
    20  	Min   time.Duration `metric:"min" type:"gauge"`
    21  	Max   time.Duration `metric:"max" type:"gauge"`
    22  	Count int64         `metric:"count" type:"counter"`
    23  	Sum   time.Duration `metric:"sum" type:"counter"`
    24  }
    25  
    26  // counter is an atomic incrementing counter which gets reset on snapshot.
    27  //
    28  // Since atomic is used to mutate the statistic the value must be 64-bit aligned.
    29  // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
    30  type counter int64
    31  
    32  func (c *counter) ptr() *int64 {
    33  	return (*int64)(c)
    34  }
    35  
    36  func (c *counter) observe(v int64) {
    37  	atomic.AddInt64(c.ptr(), v)
    38  }
    39  
    40  func (c *counter) snapshot() int64 {
    41  	return atomic.SwapInt64(c.ptr(), 0)
    42  }
    43  
    44  // gauge is an atomic integer that may be set to any arbitrary value, the value
    45  // does not change after a snapshot.
    46  //
    47  // Since atomic is used to mutate the statistic the value must be 64-bit aligned.
    48  // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
    49  type gauge int64
    50  
    51  func (g *gauge) ptr() *int64 {
    52  	return (*int64)(g)
    53  }
    54  
    55  func (g *gauge) observe(v int64) {
    56  	atomic.StoreInt64(g.ptr(), v)
    57  }
    58  
    59  func (g *gauge) snapshot() int64 {
    60  	return atomic.LoadInt64(g.ptr())
    61  }
    62  
    63  // minimum is an atomic integral type that keeps track of the minimum of all
    64  // values that it observed between snapshots.
    65  //
    66  // Since atomic is used to mutate the statistic the value must be 64-bit aligned.
    67  // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
    68  type minimum int64
    69  
    70  func (m *minimum) ptr() *int64 {
    71  	return (*int64)(m)
    72  }
    73  
    74  func (m *minimum) observe(v int64) {
    75  	for {
    76  		ptr := m.ptr()
    77  		min := atomic.LoadInt64(ptr)
    78  
    79  		if min >= 0 && min <= v {
    80  			break
    81  		}
    82  
    83  		if atomic.CompareAndSwapInt64(ptr, min, v) {
    84  			break
    85  		}
    86  	}
    87  }
    88  
    89  func (m *minimum) snapshot() int64 {
    90  	p := m.ptr()
    91  	v := atomic.LoadInt64(p)
    92  	atomic.CompareAndSwapInt64(p, v, -1)
    93  	if v < 0 {
    94  		v = 0
    95  	}
    96  	return v
    97  }
    98  
    99  // maximum is an atomic integral type that keeps track of the maximum of all
   100  // values that it observed between snapshots.
   101  //
   102  // Since atomic is used to mutate the statistic the value must be 64-bit aligned.
   103  // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
   104  type maximum int64
   105  
   106  func (m *maximum) ptr() *int64 {
   107  	return (*int64)(m)
   108  }
   109  
   110  func (m *maximum) observe(v int64) {
   111  	for {
   112  		ptr := m.ptr()
   113  		max := atomic.LoadInt64(ptr)
   114  
   115  		if max >= 0 && max >= v {
   116  			break
   117  		}
   118  
   119  		if atomic.CompareAndSwapInt64(ptr, max, v) {
   120  			break
   121  		}
   122  	}
   123  }
   124  
   125  func (m *maximum) snapshot() int64 {
   126  	p := m.ptr()
   127  	v := atomic.LoadInt64(p)
   128  	atomic.CompareAndSwapInt64(p, v, -1)
   129  	if v < 0 {
   130  		v = 0
   131  	}
   132  	return v
   133  }
   134  
   135  type summary struct {
   136  	min   minimum
   137  	max   maximum
   138  	sum   counter
   139  	count counter
   140  }
   141  
   142  func makeSummary() summary {
   143  	return summary{
   144  		min: -1,
   145  		max: -1,
   146  	}
   147  }
   148  
   149  func (s *summary) observe(v int64) {
   150  	s.min.observe(v)
   151  	s.max.observe(v)
   152  	s.sum.observe(v)
   153  	s.count.observe(1)
   154  }
   155  
   156  func (s *summary) observeDuration(v time.Duration) {
   157  	s.observe(int64(v))
   158  }
   159  
   160  func (s *summary) snapshot() SummaryStats {
   161  	avg := int64(0)
   162  	min := s.min.snapshot()
   163  	max := s.max.snapshot()
   164  	sum := s.sum.snapshot()
   165  	count := s.count.snapshot()
   166  
   167  	if count != 0 {
   168  		avg = int64(float64(sum) / float64(count))
   169  	}
   170  
   171  	return SummaryStats{
   172  		Avg:   avg,
   173  		Min:   min,
   174  		Max:   max,
   175  		Count: count,
   176  		Sum:   sum,
   177  	}
   178  }
   179  
   180  func (s *summary) snapshotDuration() DurationStats {
   181  	summary := s.snapshot()
   182  	return DurationStats{
   183  		Avg:   time.Duration(summary.Avg),
   184  		Min:   time.Duration(summary.Min),
   185  		Max:   time.Duration(summary.Max),
   186  		Count: summary.Count,
   187  		Sum:   time.Duration(summary.Sum),
   188  	}
   189  }