gosora/common/counters/performance.go
Azareal 459d745cb1 initial perf anaytics
add tasks to debug page
ignore .git on debug page for speed

add perfchunks table

Renamed phrases (changed statistics to stats):
panel_menu_stats
panel_menu_stats_posts
panel_menu_stats_topics
panel_menu_stats_forums
panel_menu_stats_routes
panel_menu_stats_agents
panel_menu_stats_systems
panel_menu_stats_languages
panel_menu_stats_referrers
panel_menu_stats_memory
panel_menu_stats_active_memory
panel_menu_stats_perf
panel_stats_views_head_suffix
panel_stats_user_agents_head
panel_stats_forums_head
panel_stats_languages_head
panel_stats_post_counts_head
panel_stats_referrers_head
panel_stats_routes_head
panel_stats_operating_systems_head
panel_stats_topic_counts_head
panel_stats_requests_head
panel_stats_memory_head
panel_stats_active_memory_head
panel_stats_spam_hide
panel_stats_spam_show
panel_stats_memory_type_total
panel_stats_memory_type_stack
panel_stats_memory_type_heap
panel_stats_time_range_one_year
panel_stats_time_range_three_months
panel_stats_time_range_one_month
panel_stats_time_range_one_week
panel_stats_time_range_two_days
panel_stats_time_range_one_day
panel_stats_time_range_twelve_hours
panel_stats_time_range_six_hours
panel_stats_post_counts_chart_aria
panel_stats_topic_counts_chart_aria
panel_stats_requests_chart_aria
panel_stats_memory_chart_aria
panel_stats_details_head
panel_stats_post_counts_table_aria
panel_stats_topic_counts_table_aria
panel_stats_route_views_table_aria
panel_stats_requests_table_aria
panel_stats_memory_table_aria
panel_stats_views_suffix
panel_stats_posts_suffix
panel_stats_topics_suffix
panel_stats_user_agents_no_user_agents
panel_stats_forums_no_forums
panel_stats_languages_no_languages
panel_stats_post_counts_no_post_counts
panel_stats_referrers_no_referrers
panel_stats_routes_no_routes
panel_stats_operating_systems_no_operating_systems
panel_stats_memory_no_memory

Added phrases:
panel_debug_tasks
panel_debug_tasks_half_second
panel_debug_tasks_second
panel_debug_tasks_fifteen_minute
panel_debug_tasks_hour
panel_debug_tasks_shutdown
panel_stats_perf_head
panel_stats_perf_low
panel_stats_perf_high
panel_stats_perf_avg

You will need to run the updater / patcher for this commit.
2020-02-23 19:08:47 +10:00

114 lines
2.6 KiB
Go

package counters
import (
"database/sql"
"sync/atomic"
"time"
"math"
c "github.com/Azareal/Gosora/common"
qgen "github.com/Azareal/Gosora/query_gen"
"github.com/pkg/errors"
)
var PerfCounter *DefaultPerfCounter
type PerfCounterBucket struct {
low *MutexCounter64Bucket
high *MutexCounter64Bucket
avg *MutexCounter64Bucket
}
// TODO: Track perf on a per route basis
type DefaultPerfCounter struct {
buckets []PerfCounterBucket
insert *sql.Stmt
}
func NewDefaultPerfCounter(acc *qgen.Accumulator) (*DefaultPerfCounter, error) {
co := &DefaultPerfCounter{
buckets: []PerfCounterBucket{
PerfCounterBucket{
low: &MutexCounter64Bucket{counter: 0},
high: &MutexCounter64Bucket{counter: 0},
avg: &MutexCounter64Bucket{counter: 0},
},
},
insert: acc.Insert("perfchunks").Columns("low,high,avg,createdAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(),
}
c.AddScheduledFifteenMinuteTask(co.Tick)
//c.AddScheduledSecondTask(co.Tick)
c.AddShutdownTask(co.Tick)
return co, acc.FirstError()
}
func (co *DefaultPerfCounter) Tick() error {
getCounter := func(b *MutexCounter64Bucket) int64 {
return atomic.SwapInt64(&b.counter, 0)
}
for _, b := range co.buckets {
low := atomic.SwapInt64(&b.low.counter, math.MaxInt64)
if low == math.MaxInt64 {
low = 0
}
high := getCounter(b.high)
avg := getCounter(b.avg)
err := co.insertChunk(low, high, avg) // TODO: Bulk insert for speed?
if err != nil {
return errors.Wrap(errors.WithStack(err), "perf counter")
}
}
return nil
}
func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error {
if low == 0 && high == 0 && avg == 0 {
return nil
}
c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg)
_, err := co.insert.Exec(low, high, avg)
return err
}
func (co *DefaultPerfCounter) Push(dur time.Duration) {
id := 0
b := co.buckets[id]
//c.DebugDetail("co.buckets[", id, "]: ", b)
micro := dur.Microseconds()
low := b.low
if micro < low.counter {
low.Lock()
if micro < low.counter {
atomic.StoreInt64(&low.counter,micro)
}
low.Unlock()
}
high := b.high
if micro > high.counter {
high.Lock()
if micro > high.counter {
atomic.StoreInt64(&high.counter,micro)
}
high.Unlock()
}
avg := b.avg
// TODO: Sync semantics are slightly loose but it should be close enough for our purposes here
if micro != avg.counter {
t := false
avg.Lock()
if avg.counter == 0 {
t = atomic.CompareAndSwapInt64(&avg.counter, 0, micro)
}
if !t && micro != avg.counter {
atomic.StoreInt64(&avg.counter,(micro+avg.counter) / 2)
}
avg.Unlock()
}
}