github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/counters/performance.go (about)

     1  package counters
     2  
     3  import (
     4  	"database/sql"
     5  	"math"
     6  	"time"
     7  
     8  	c "github.com/Azareal/Gosora/common"
     9  	qgen "github.com/Azareal/Gosora/query_gen"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  var PerfCounter *DefaultPerfCounter
    14  
    15  type PerfCounterBucket struct {
    16  	low  *MutexCounter64Bucket
    17  	high *MutexCounter64Bucket
    18  	avg  *MutexCounter64Bucket
    19  }
    20  
    21  // TODO: Track perf on a per route basis
    22  type DefaultPerfCounter struct {
    23  	buckets []*PerfCounterBucket
    24  
    25  	insert *sql.Stmt
    26  }
    27  
    28  func NewDefaultPerfCounter(acc *qgen.Accumulator) (*DefaultPerfCounter, error) {
    29  	co := &DefaultPerfCounter{
    30  		buckets: []*PerfCounterBucket{
    31  			{
    32  				low:  &MutexCounter64Bucket{counter: math.MaxInt64},
    33  				high: &MutexCounter64Bucket{counter: 0},
    34  				avg:  &MutexCounter64Bucket{counter: 0},
    35  			},
    36  		},
    37  		insert: acc.Insert("perfchunks").Columns("low,high,avg,createdAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(),
    38  	}
    39  
    40  	c.Tasks.FifteenMin.Add(co.Tick)
    41  	//c.Tasks.Sec.Add(co.Tick)
    42  	c.Tasks.Shutdown.Add(co.Tick)
    43  	return co, acc.FirstError()
    44  }
    45  
    46  func (co *DefaultPerfCounter) Tick() error {
    47  	getCounter := func(b *MutexCounter64Bucket) (c int64) {
    48  		b.Lock()
    49  		c = b.counter
    50  		b.counter = 0
    51  		b.Unlock()
    52  		return c
    53  	}
    54  	var low int64
    55  	hTbl := c.GetHookTable()
    56  	for _, b := range co.buckets {
    57  		b.low.Lock()
    58  		low, b.low.counter = b.low.counter, math.MaxInt64
    59  		b.low.Unlock()
    60  		if low == math.MaxInt64 {
    61  			low = 0
    62  		}
    63  		high := getCounter(b.high)
    64  		avg := getCounter(b.avg)
    65  		c.H_counters_perf_tick_row_hook(hTbl, low, high, avg)
    66  		if e := co.insertChunk(low, high, avg); e != nil { // TODO: Bulk insert for speed?
    67  			return errors.Wrap(errors.WithStack(e), "perf counter")
    68  		}
    69  	}
    70  	return nil
    71  }
    72  
    73  func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error {
    74  	if low == 0 && high == 0 && avg == 0 {
    75  		return nil
    76  	}
    77  	c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg)
    78  	if c.Dev.LogNewLongRoute && high > (5*1000*1000) {
    79  		c.Logf("pchunk high %d", high)
    80  	}
    81  	_, e := co.insert.Exec(low, high, avg)
    82  	return e
    83  }
    84  
    85  func (co *DefaultPerfCounter) Push(dur time.Duration /*,_ bool*/) {
    86  	id := 0
    87  	b := co.buckets[id]
    88  	//c.DebugDetail("buckets ", id, ": ", b)
    89  	micro := dur.Microseconds()
    90  	if micro >= math.MaxInt32 {
    91  		c.LogWarning(errors.New("dur should not be int32 max or higher"))
    92  	}
    93  
    94  	low := b.low
    95  	low.Lock()
    96  	if micro < low.counter {
    97  		low.counter = micro
    98  	}
    99  	low.Unlock()
   100  
   101  	high := b.high
   102  	high.Lock()
   103  	if micro > high.counter {
   104  		high.counter = micro
   105  	}
   106  	high.Unlock()
   107  
   108  	avg := b.avg
   109  	avg.Lock()
   110  	if micro != avg.counter {
   111  		if avg.counter == 0 {
   112  			avg.counter = micro
   113  		} else {
   114  			avg.counter = (micro + avg.counter) / 2
   115  		}
   116  	}
   117  	avg.Unlock()
   118  }