package counters import ( "database/sql" c "github.com/Azareal/Gosora/common" qgen "github.com/Azareal/Gosora/query_gen" "github.com/pkg/errors" ) var LangViewCounter *DefaultLangViewCounter var langCodes = []string{ "unknown", "none", "af", "ar", "az", "be", "bg", "bs", "ca", "cs", "cy", "da", "de", "dv", "el", "en", "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "gl", "gu", "he", "hi", "hr", "hu", "hy", "id", "is", "it", "ja", "ka", "kk", "kn", "ko", "kok", "ky", "lt", "lv", "mi", "mk", "mn", "mr", "ms", "mt", "nb", "nl", "nn", "ns", "pa", "pl", "ps", "pt", "qu", "ro", "ru", "sa", "se", "sk", "sl", "sq", "sr", "sv", "sw", "syr", "ta", "te", "th", "tl", "tn", "tr", "tt", "ts", "uk", "ur", "uz", "vi", "xh", "zh", "zu", } type DefaultLangViewCounter struct { buckets []*RWMutexCounterBucket //[OSID]count codesToIndices map[string]int insert *sql.Stmt } func NewDefaultLangViewCounter(acc *qgen.Accumulator) (*DefaultLangViewCounter, error) { langBuckets := make([]*RWMutexCounterBucket, len(langCodes)) for bucketID, _ := range langBuckets { langBuckets[bucketID] = &RWMutexCounterBucket{counter: 0} } codesToIndices := make(map[string]int, len(langCodes)) for index, code := range langCodes { codesToIndices[code] = index } co := &DefaultLangViewCounter{ buckets: langBuckets, codesToIndices: codesToIndices, insert: acc.Insert("viewchunks_langs").Columns("count, createdAt, lang").Fields("?,UTC_TIMESTAMP(),?").Prepare(), } c.AddScheduledFifteenMinuteTask(co.Tick) //c.AddScheduledSecondTask(co.Tick) c.AddShutdownTask(co.Tick) return co, acc.FirstError() } func (co *DefaultLangViewCounter) Tick() error { for id, bucket := range co.buckets { var count int bucket.RLock() count = bucket.counter bucket.counter = 0 // TODO: Add a SetZero method to reduce the amount of duplicate code between the OS and agent counters? bucket.RUnlock() err := co.insertChunk(count, id) // TODO: Bulk insert for speed? if err != nil { return errors.Wrap(errors.WithStack(err), "langview counter") } } return nil } func (co *DefaultLangViewCounter) insertChunk(count int, id int) error { if count == 0 { return nil } langCode := langCodes[id] c.DebugLogf("Inserting a vchunk with a count of %d for lang %s (%d)", count, langCode, id) _, err := co.insert.Exec(count, langCode) return err } func (co *DefaultLangViewCounter) Bump(langCode string) (validCode bool) { validCode = true id, ok := co.codesToIndices[langCode] if !ok { // TODO: Tell the caller that the code's invalid id = 0 // Unknown validCode = false } // TODO: Test this check c.DebugDetail("co.buckets[", id, "]: ", co.buckets[id]) if len(co.buckets) <= id || id < 0 { return validCode } co.buckets[id].Lock() co.buckets[id].counter++ co.buckets[id].Unlock() return validCode }