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 }