fix the instance crashing after deleting a topic due to a topic view counter error
simplify the topic view counter
This commit is contained in:
parent
99bc972ad2
commit
6d805a13cf
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
c "github.com/Azareal/Gosora/common"
|
c "github.com/Azareal/Gosora/common"
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TopicViewCounter *DefaultTopicViewCounter
|
var TopicViewCounter *DefaultTopicViewCounter
|
||||||
|
@ -23,68 +24,59 @@ type DefaultTopicViewCounter struct {
|
||||||
|
|
||||||
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
||||||
acc := qgen.NewAcc()
|
acc := qgen.NewAcc()
|
||||||
counter := &DefaultTopicViewCounter{
|
co := &DefaultTopicViewCounter{
|
||||||
oddTopics: make(map[int]*RWMutexCounterBucket),
|
oddTopics: make(map[int]*RWMutexCounterBucket),
|
||||||
evenTopics: make(map[int]*RWMutexCounterBucket),
|
evenTopics: make(map[int]*RWMutexCounterBucket),
|
||||||
update: acc.Update("topics").Set("views = views + ?").Where("tid = ?").Prepare(),
|
update: acc.Update("topics").Set("views = views + ?").Where("tid = ?").Prepare(),
|
||||||
}
|
}
|
||||||
c.AddScheduledFifteenMinuteTask(counter.Tick) // Who knows how many topics we have queued up, we probably don't want this running too frequently
|
c.AddScheduledFifteenMinuteTask(co.Tick) // Who knows how many topics we have queued up, we probably don't want this running too frequently
|
||||||
//c.AddScheduledSecondTask(counter.Tick)
|
//c.AddScheduledSecondTask(co.Tick)
|
||||||
c.AddShutdownTask(counter.Tick)
|
c.AddShutdownTask(co.Tick)
|
||||||
return counter, acc.FirstError()
|
return co, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (counter *DefaultTopicViewCounter) Tick() error {
|
func (co *DefaultTopicViewCounter) Tick() error {
|
||||||
// TODO: Fold multiple 1 view topics into one query
|
// TODO: Fold multiple 1 view topics into one query
|
||||||
|
|
||||||
counter.oddLock.RLock()
|
cLoop := func(l *sync.RWMutex, m map[int]*RWMutexCounterBucket) error {
|
||||||
oddTopics := counter.oddTopics
|
l.RLock()
|
||||||
counter.oddLock.RUnlock()
|
for topicID, topic := range m {
|
||||||
for topicID, topic := range oddTopics {
|
l.RUnlock()
|
||||||
var count int
|
var count int
|
||||||
topic.RLock()
|
topic.RLock()
|
||||||
count = topic.counter
|
count = topic.counter
|
||||||
topic.RUnlock()
|
topic.RUnlock()
|
||||||
// TODO: Only delete the bucket when it's zero to avoid hitting popular topics?
|
// TODO: Only delete the bucket when it's zero to avoid hitting popular topics?
|
||||||
counter.oddLock.Lock()
|
l.Lock()
|
||||||
delete(counter.oddTopics, topicID)
|
delete(m, topicID)
|
||||||
counter.oddLock.Unlock()
|
l.Unlock()
|
||||||
err := counter.insertChunk(count, topicID)
|
err := co.insertChunk(count, topicID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(errors.WithStack(err),"topicview counter")
|
||||||
}
|
}
|
||||||
|
l.RLock()
|
||||||
}
|
}
|
||||||
|
l.RUnlock()
|
||||||
counter.evenLock.RLock()
|
|
||||||
evenTopics := counter.evenTopics
|
|
||||||
counter.evenLock.RUnlock()
|
|
||||||
for topicID, topic := range evenTopics {
|
|
||||||
var count int
|
|
||||||
topic.RLock()
|
|
||||||
count = topic.counter
|
|
||||||
topic.RUnlock()
|
|
||||||
// TODO: Only delete the bucket when it's zero to avoid hitting popular topics?
|
|
||||||
counter.evenLock.Lock()
|
|
||||||
delete(counter.evenTopics, topicID)
|
|
||||||
counter.evenLock.Unlock()
|
|
||||||
err := counter.insertChunk(count, topicID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
err := cLoop(&co.oddLock,co.oddTopics)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cLoop(&co.evenLock,co.evenTopics)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Optimise this further. E.g. Using IN() on every one view topic. Rinse and repeat for two views, three views, four views and five views.
|
// TODO: Optimise this further. E.g. Using IN() on every one view topic. Rinse and repeat for two views, three views, four views and five views.
|
||||||
func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) error {
|
func (co *DefaultTopicViewCounter) insertChunk(count int, topicID int) error {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.DebugLogf("Inserting %d views into topic %d", count, topicID)
|
c.DebugLogf("Inserting %d views into topic %d", count, topicID)
|
||||||
_, err := counter.update.Exec(count, topicID)
|
_, err := co.update.Exec(count, topicID)
|
||||||
if err != nil {
|
if err == sql.ErrNoRows {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +84,9 @@ func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) erro
|
||||||
tcache := c.Topics.GetCache()
|
tcache := c.Topics.GetCache()
|
||||||
if tcache != nil {
|
if tcache != nil {
|
||||||
topic, err := tcache.Get(topicID)
|
topic, err := tcache.Get(topicID)
|
||||||
if err != nil {
|
if err == sql.ErrNoRows {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
atomic.AddInt64(&topic.ViewCount, int64(count))
|
atomic.AddInt64(&topic.ViewCount, int64(count))
|
||||||
|
@ -101,34 +95,34 @@ func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (counter *DefaultTopicViewCounter) Bump(topicID int) {
|
func (co *DefaultTopicViewCounter) Bump(topicID int) {
|
||||||
// Is the ID even?
|
// Is the ID even?
|
||||||
if topicID%2 == 0 {
|
if topicID%2 == 0 {
|
||||||
counter.evenLock.RLock()
|
co.evenLock.RLock()
|
||||||
topic, ok := counter.evenTopics[topicID]
|
topic, ok := co.evenTopics[topicID]
|
||||||
counter.evenLock.RUnlock()
|
co.evenLock.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
topic.Lock()
|
topic.Lock()
|
||||||
topic.counter++
|
topic.counter++
|
||||||
topic.Unlock()
|
topic.Unlock()
|
||||||
} else {
|
} else {
|
||||||
counter.evenLock.Lock()
|
co.evenLock.Lock()
|
||||||
counter.evenTopics[topicID] = &RWMutexCounterBucket{counter: 1}
|
co.evenTopics[topicID] = &RWMutexCounterBucket{counter: 1}
|
||||||
counter.evenLock.Unlock()
|
co.evenLock.Unlock()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
counter.oddLock.RLock()
|
co.oddLock.RLock()
|
||||||
topic, ok := counter.oddTopics[topicID]
|
topic, ok := co.oddTopics[topicID]
|
||||||
counter.oddLock.RUnlock()
|
co.oddLock.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
topic.Lock()
|
topic.Lock()
|
||||||
topic.counter++
|
topic.counter++
|
||||||
topic.Unlock()
|
topic.Unlock()
|
||||||
} else {
|
} else {
|
||||||
counter.oddLock.Lock()
|
co.oddLock.Lock()
|
||||||
counter.oddTopics[topicID] = &RWMutexCounterBucket{counter: 1}
|
co.oddTopics[topicID] = &RWMutexCounterBucket{counter: 1}
|
||||||
counter.oddLock.Unlock()
|
co.oddLock.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue