gosora/common/counters/routes.go

70 lines
1.9 KiB
Go

package counters
import (
"database/sql"
c "github.com/Azareal/Gosora/common"
qgen "github.com/Azareal/Gosora/query_gen"
"github.com/pkg/errors"
)
var RouteViewCounter *DefaultRouteViewCounter
// TODO: Make this lockless?
type DefaultRouteViewCounter struct {
buckets []*RWMutexCounterBucket //[RouteID]count
insert *sql.Stmt
}
func NewDefaultRouteViewCounter(acc *qgen.Accumulator) (*DefaultRouteViewCounter, error) {
routeBuckets := make([]*RWMutexCounterBucket, len(routeMapEnum))
for bucketID, _ := range routeBuckets {
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
}
co := &DefaultRouteViewCounter{
buckets: routeBuckets,
insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
}
c.AddScheduledFifteenMinuteTask(co.Tick) // There could be a lot of routes, so we don't want to be running this every second
//c.AddScheduledSecondTask(co.Tick)
c.AddShutdownTask(co.Tick)
return co, acc.FirstError()
}
func (co *DefaultRouteViewCounter) Tick() error {
for routeID, routeBucket := range co.buckets {
var count int
routeBucket.RLock()
count = routeBucket.counter
routeBucket.counter = 0
routeBucket.RUnlock()
err := co.insertChunk(count, routeID) // TODO: Bulk insert for speed?
if err != nil {
return errors.Wrap(errors.WithStack(err), "route counter")
}
}
return nil
}
func (co *DefaultRouteViewCounter) insertChunk(count int, route int) error {
if count == 0 {
return nil
}
routeName := reverseRouteMapEnum[route]
c.DebugLogf("Inserting a vchunk with a count of %d for route %s (%d)", count, routeName, route)
_, err := co.insert.Exec(count, routeName)
return err
}
func (co *DefaultRouteViewCounter) Bump(route int) {
// TODO: Test this check
c.DebugDetail("co.buckets[", route, "]: ", co.buckets[route])
if len(co.buckets) <= route || route < 0 {
return
}
co.buckets[route].Lock()
co.buckets[route].counter++
co.buckets[route].Unlock()
}