mirror of https://github.com/rocboss/paopao-ce
Merge pull request #78 from alimy/pr-cache
optimize add cache first number page of post in custom configurepull/80/head
commit
70e1c58bb5
@ -1,6 +1,38 @@
|
||||
package core
|
||||
|
||||
// CacheService cache service interface that implement base Redis or other
|
||||
type CacheService interface {
|
||||
// TODO
|
||||
import (
|
||||
"github.com/rocboss/paopao-ce/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
IdxActNop IndexActionT = iota + 1
|
||||
IdxActCreatePost
|
||||
IdxActUpdatePost
|
||||
IdxActDeletePost
|
||||
IdxActStickPost
|
||||
)
|
||||
|
||||
type IndexActionT uint8
|
||||
|
||||
func (a IndexActionT) String() string {
|
||||
switch a {
|
||||
case IdxActNop:
|
||||
return "no operator"
|
||||
case IdxActCreatePost:
|
||||
return "create post"
|
||||
case IdxActUpdatePost:
|
||||
return "update post"
|
||||
case IdxActDeletePost:
|
||||
return "delete post"
|
||||
case IdxActStickPost:
|
||||
return "stick post"
|
||||
default:
|
||||
return "unknow action"
|
||||
}
|
||||
}
|
||||
|
||||
// CacheIndexService cache index service interface
|
||||
type CacheIndexService interface {
|
||||
IndexPosts(offset int, limit int) ([]*model.PostFormated, bool)
|
||||
SendAction(active IndexActionT)
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/rocboss/paopao-ce/internal/conf"
|
||||
"github.com/rocboss/paopao-ce/internal/core"
|
||||
"github.com/rocboss/paopao-ce/internal/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func newSimpleCacheIndexServant(getIndexPosts func(offset, limit int) ([]*model.PostFormated, error)) *simpleCacheIndexServant {
|
||||
s := conf.CacheIndexSetting
|
||||
cacheIndex := &simpleCacheIndexServant{
|
||||
getIndexPosts: getIndexPosts,
|
||||
maxIndexSize: s.MaxIndexSize,
|
||||
indexPosts: make([]*model.PostFormated, 0),
|
||||
indexActionCh: make(chan core.IndexActionT, 100), // optimize: size need configure by custom
|
||||
checkTick: time.NewTicker(time.Duration(s.CheckTickDuration) * time.Second), // check whether need update index every 1 minute
|
||||
expireIndexTick: time.NewTicker(time.Duration(s.ExpireTickDuration) * time.Second), // force expire index every 5 minute
|
||||
}
|
||||
|
||||
// start index posts
|
||||
cacheIndex.atomicIndex.Store(cacheIndex.indexPosts)
|
||||
go cacheIndex.startIndexPosts()
|
||||
|
||||
return cacheIndex
|
||||
}
|
||||
|
||||
func (s *simpleCacheIndexServant) IndexPosts(offset int, limit int) ([]*model.PostFormated, bool) {
|
||||
posts := s.atomicIndex.Load().([]*model.PostFormated)
|
||||
start := offset * limit
|
||||
end := start + limit
|
||||
if len(posts) >= end {
|
||||
logrus.Debugln("get index posts from cached")
|
||||
return posts[start:end], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *simpleCacheIndexServant) SendAction(act core.IndexActionT) {
|
||||
select {
|
||||
case s.indexActionCh <- act:
|
||||
logrus.Debugf("send indexAction by chan: %s", act)
|
||||
default:
|
||||
go func(ch chan<- core.IndexActionT, act core.IndexActionT) {
|
||||
logrus.Debugf("send indexAction by goroutine: %s", act)
|
||||
ch <- act
|
||||
}(s.indexActionCh, act)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simpleCacheIndexServant) startIndexPosts() {
|
||||
var err error
|
||||
for {
|
||||
select {
|
||||
case <-s.checkTick.C:
|
||||
if len(s.indexPosts) == 0 {
|
||||
logrus.Debugf("index posts by checkTick")
|
||||
if s.indexPosts, err = s.getIndexPosts(0, s.maxIndexSize); err == nil {
|
||||
s.atomicIndex.Store(s.indexPosts)
|
||||
} else {
|
||||
logrus.Errorf("get index posts err: %v", err)
|
||||
}
|
||||
}
|
||||
case <-s.expireIndexTick.C:
|
||||
logrus.Debugf("expire index posts by expireIndexTick")
|
||||
if len(s.indexPosts) != 0 {
|
||||
s.indexPosts = nil
|
||||
s.atomicIndex.Store(s.indexPosts)
|
||||
}
|
||||
case action := <-s.indexActionCh:
|
||||
switch action {
|
||||
case core.IdxActCreatePost, core.IdxActUpdatePost, core.IdxActDeletePost, core.IdxActStickPost:
|
||||
// prevent many update post in least time
|
||||
if len(s.indexPosts) != 0 {
|
||||
logrus.Debugf("remove index posts by action %s", action)
|
||||
s.indexPosts = nil
|
||||
s.atomicIndex.Store(s.indexPosts)
|
||||
}
|
||||
default:
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"github.com/rocboss/paopao-ce/internal/core"
|
||||
"github.com/rocboss/paopao-ce/internal/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (d *dataServant) IndexPosts(offset int, limit int) ([]*model.PostFormated, error) {
|
||||
if d.useCacheIndex {
|
||||
if posts, ok := d.cacheIndex.IndexPosts(offset, limit); ok {
|
||||
logrus.Debugln("get index posts from cached")
|
||||
return posts, nil
|
||||
}
|
||||
}
|
||||
logrus.Debugf("get index posts from database but useCacheIndex: %t", d.useCacheIndex)
|
||||
return d.getIndexPosts(offset, limit)
|
||||
}
|
||||
|
||||
func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) {
|
||||
postIds := make([]int64, 0, len(posts))
|
||||
userIds := make([]int64, 0, len(posts))
|
||||
for _, post := range posts {
|
||||
postIds = append(postIds, post.ID)
|
||||
userIds = append(userIds, post.UserID)
|
||||
}
|
||||
|
||||
postContents, err := d.GetPostContentsByIDs(postIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users, err := d.GetUsersByIDs(userIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userMap := make(map[int64]*model.UserFormated, len(users))
|
||||
for _, user := range users {
|
||||
userMap[user.ID] = user.Format()
|
||||
}
|
||||
|
||||
contentMap := make(map[int64][]*model.PostContentFormated, len(postContents))
|
||||
for _, content := range postContents {
|
||||
contentMap[content.PostID] = append(contentMap[content.PostID], content.Format())
|
||||
}
|
||||
|
||||
// 数据整合
|
||||
postsFormated := make([]*model.PostFormated, 0, len(posts))
|
||||
for _, post := range posts {
|
||||
postFormated := post.Format()
|
||||
postFormated.User = userMap[post.UserID]
|
||||
postFormated.Contents = contentMap[post.ID]
|
||||
postsFormated = append(postsFormated, postFormated)
|
||||
}
|
||||
return postsFormated, nil
|
||||
}
|
||||
|
||||
func (d *dataServant) getIndexPosts(offset int, limit int) ([]*model.PostFormated, error) {
|
||||
posts, err := (&model.Post{}).List(d.engine, &model.ConditionsT{
|
||||
"ORDER": "is_top DESC, latest_replied_on DESC",
|
||||
}, offset, limit)
|
||||
if err != nil {
|
||||
logrus.Debugf("getIndexPosts err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return d.MergePosts(posts)
|
||||
}
|
||||
|
||||
func (d *dataServant) indexActive(act core.IndexActionT) {
|
||||
if d.useCacheIndex {
|
||||
d.cacheIndex.SendAction(act)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue