2018-02-19 04:26:01 +00:00
package counters
2018-02-04 08:15:20 +00:00
import (
2018-02-05 10:29:13 +00:00
"database/sql"
2018-02-04 08:15:20 +00:00
"sync"
"sync/atomic"
2018-02-05 10:29:13 +00:00
2018-02-19 04:26:01 +00:00
".."
"../../query_gen/lib"
2018-02-04 08:15:20 +00:00
)
2018-02-05 10:29:13 +00:00
var ReferrerTracker * DefaultReferrerTracker
2018-02-04 08:15:20 +00:00
2018-02-05 10:29:13 +00:00
// Add ReferrerItems here after they've had zero views for a while
var referrersToDelete = make ( map [ string ] * ReferrerItem )
2018-02-04 08:15:20 +00:00
type ReferrerItem struct {
2018-02-05 10:29:13 +00:00
Count int64
2018-02-04 08:15:20 +00:00
}
// ? We'll track referrer domains here rather than the exact URL they arrived from for now, we'll think about expanding later
// ? Referrers are fluid and ever-changing so we have to use string keys rather than 'enum' ints
type DefaultReferrerTracker struct {
odd map [ string ] * ReferrerItem
even map [ string ] * ReferrerItem
oddLock sync . RWMutex
evenLock sync . RWMutex
2018-02-05 10:29:13 +00:00
insert * sql . Stmt
2018-02-04 08:15:20 +00:00
}
2018-02-05 10:29:13 +00:00
func NewDefaultReferrerTracker ( ) ( * DefaultReferrerTracker , error ) {
2018-08-04 11:46:36 +00:00
acc := qgen . NewAcc ( )
2018-02-05 10:29:13 +00:00
refTracker := & DefaultReferrerTracker {
odd : make ( map [ string ] * ReferrerItem ) ,
even : make ( map [ string ] * ReferrerItem ) ,
insert : acc . Insert ( "viewchunks_referrers" ) . Columns ( "count, createdAt, domain" ) . Fields ( "?,UTC_TIMESTAMP(),?" ) . Prepare ( ) , // TODO: Do something more efficient than doing a query for each referrer
2018-02-04 08:15:20 +00:00
}
2018-02-19 04:26:01 +00:00
common . AddScheduledFifteenMinuteTask ( refTracker . Tick )
//common.AddScheduledSecondTask(refTracker.Tick)
common . AddShutdownTask ( refTracker . Tick )
2018-02-05 10:29:13 +00:00
return refTracker , acc . FirstError ( )
2018-02-04 08:15:20 +00:00
}
2018-02-05 10:29:13 +00:00
// TODO: Move this and the other view tickers out of the main task loop to avoid blocking other tasks?
2018-02-04 08:15:20 +00:00
func ( ref * DefaultReferrerTracker ) Tick ( ) ( err error ) {
2018-02-05 10:29:13 +00:00
for referrer , counter := range referrersToDelete {
// Handle views which squeezed through the gaps at the last moment
count := counter . Count
if count != 0 {
err := ref . insertChunk ( referrer , count ) // TODO: Bulk insert for speed?
if err != nil {
return err
}
}
delete ( referrersToDelete , referrer )
2018-02-04 08:15:20 +00:00
}
2018-02-05 10:29:13 +00:00
// Run the queries and schedule zero view refs for deletion from memory
ref . oddLock . Lock ( )
for referrer , counter := range ref . odd {
if counter . Count == 0 {
referrersToDelete [ referrer ] = counter
delete ( ref . odd , referrer )
}
count := atomic . SwapInt64 ( & counter . Count , 0 )
err := ref . insertChunk ( referrer , count ) // TODO: Bulk insert for speed?
if err != nil {
return err
}
}
ref . oddLock . Unlock ( )
ref . evenLock . Lock ( )
for referrer , counter := range ref . even {
if counter . Count == 0 {
referrersToDelete [ referrer ] = counter
delete ( ref . even , referrer )
}
count := atomic . SwapInt64 ( & counter . Count , 0 )
err := ref . insertChunk ( referrer , count ) // TODO: Bulk insert for speed?
if err != nil {
return err
}
}
ref . evenLock . Unlock ( )
2018-02-04 08:15:20 +00:00
return nil
}
2018-02-05 10:29:13 +00:00
func ( ref * DefaultReferrerTracker ) insertChunk ( referrer string , count int64 ) error {
if count == 0 {
return nil
}
2018-02-19 04:26:01 +00:00
common . DebugDetailf ( "Inserting a viewchunk with a count of %d for referrer %s" , count , referrer )
2018-02-05 10:29:13 +00:00
_ , err := ref . insert . Exec ( count , referrer )
return err
}
2018-02-04 08:15:20 +00:00
func ( ref * DefaultReferrerTracker ) Bump ( referrer string ) {
if referrer == "" {
return
}
var refItem * ReferrerItem
// Slightly crude and rudimentary, but it should give a basic degree of sharding
if referrer [ 0 ] % 2 == 0 {
ref . evenLock . RLock ( )
refItem = ref . even [ referrer ]
ref . evenLock . RUnlock ( )
2018-02-05 10:29:13 +00:00
if refItem != nil {
atomic . AddInt64 ( & refItem . Count , 1 )
2018-02-04 08:15:20 +00:00
} else {
ref . evenLock . Lock ( )
2018-02-05 10:29:13 +00:00
ref . even [ referrer ] = & ReferrerItem { Count : 1 }
2018-02-04 08:15:20 +00:00
ref . evenLock . Unlock ( )
}
} else {
ref . oddLock . RLock ( )
refItem = ref . odd [ referrer ]
ref . oddLock . RUnlock ( )
2018-02-05 10:29:13 +00:00
if refItem != nil {
atomic . AddInt64 ( & refItem . Count , 1 )
2018-02-04 08:15:20 +00:00
} else {
ref . oddLock . Lock ( )
2018-02-05 10:29:13 +00:00
ref . odd [ referrer ] = & ReferrerItem { Count : 1 }
2018-02-04 08:15:20 +00:00
ref . oddLock . Unlock ( )
}
}
}