完善缓存

This commit is contained in:
xing 2022-09-27 21:52:15 +08:00
parent 086de50dcf
commit fa7fc10a6c
7 changed files with 160 additions and 20 deletions

View File

@ -5,6 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"github/fthvgb1/wp-go/cache" "github/fthvgb1/wp-go/cache"
"github/fthvgb1/wp-go/helper"
"github/fthvgb1/wp-go/logs" "github/fthvgb1/wp-go/logs"
"github/fthvgb1/wp-go/models" "github/fthvgb1/wp-go/models"
"github/fthvgb1/wp-go/vars" "github/fthvgb1/wp-go/vars"
@ -20,6 +21,7 @@ var recentPostsCaches *cache.SliceCache[models.WpPosts]
var recentCommentsCaches *cache.SliceCache[models.WpComments] var recentCommentsCaches *cache.SliceCache[models.WpComments]
var postCommentCaches *cache.MapCache[uint64, []models.WpComments] var postCommentCaches *cache.MapCache[uint64, []models.WpComments]
var postsCache *cache.MapCache[uint64, models.WpPosts] var postsCache *cache.MapCache[uint64, models.WpPosts]
var monthPostsCache *cache.MapCache[string, []uint64]
func InitActionsCommonCache() { func InitActionsCommonCache() {
archivesCaches = &Arch{ archivesCaches = &Arch{
@ -27,9 +29,12 @@ func InitActionsCommonCache() {
setCacheFunc: archives, setCacheFunc: archives,
} }
monthPostsCache = cache.NewMapCache[string, []uint64](monthPost, time.Hour)
postContextCache = cache.NewMapCache[uint64, PostContext](getPostContext, vars.Conf.ContextPostCacheTime) postContextCache = cache.NewMapCache[uint64, PostContext](getPostContext, vars.Conf.ContextPostCacheTime)
postsCache = cache.NewMapBatchCache[uint64, models.WpPosts](getPosts, time.Hour) postsCache = cache.NewMapBatchCache[uint64, models.WpPosts](getPosts, time.Hour)
postsCache.SetCacheFunc(getPost)
categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime) categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime)
@ -39,6 +44,45 @@ func InitActionsCommonCache() {
postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5) postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5)
} }
func GetMonthPostIds(ctx context.Context, year, month string, page, limit int, order string) (r []models.WpPosts, total int, err error) {
res, err := monthPostsCache.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month)
if err != nil {
return
}
if order == "desc" {
res = helper.SliceReverse(res)
}
total = len(res)
rr := helper.SlicePagination(res, page, limit)
r, err = GetPosts(ctx, rr)
return
}
func monthPost(args ...any) (r []uint64, err error) {
year, month := args[0].(string), args[1].(string)
where := models.SqlBuilder{
{"post_type", "in", ""},
{"post_status", "in", ""},
{"year(post_date)", year},
{"month(post_date)", month},
}
postType := []any{"post"}
status := []any{"publish"}
ids, err := models.Find[models.WpPosts](where, "ID", "", models.SqlBuilder{{"Id", "asc"}}, nil, 0, postType, status)
if err != nil {
return
}
for _, post := range ids {
r = append(r, post.Id)
}
return
}
type PostIds struct {
Ids []uint64
Length int
}
type Arch struct { type Arch struct {
data []models.PostArchive data []models.PostArchive
mutex *sync.Mutex mutex *sync.Mutex

View File

@ -9,15 +9,19 @@ import (
"time" "time"
) )
func GetPostAndCache(ctx context.Context, id uint64, ids ...uint64) (models.WpPosts, error) { func GetPostAndCache(ctx context.Context, id uint64) (models.WpPosts, error) {
return postsCache.GetCacheBatch(ctx, id, time.Second, ids) return postsCache.GetCache(ctx, id, time.Second, id)
} }
func GetPost(id uint64) models.WpPosts { func GetPost(id uint64) models.WpPosts {
return postsCache.Get(id) return postsCache.Get(id)
} }
func GetPosts(ctx context.Context, ids []uint64) ([]models.WpPosts, error) {
return postsCache.GetCacheBatch(ctx, ids, time.Second, ids)
}
func SetPostCache(ids []models.WpPosts) error { func SetPostCache(ids []models.WpPosts) error {
var arg []uint64 var arg []uint64
for _, posts := range ids { for _, posts := range ids {
@ -26,6 +30,16 @@ func SetPostCache(ids []models.WpPosts) error {
return postsCache.SetByBatchFn(arg) return postsCache.SetByBatchFn(arg)
} }
func getPost(id ...any) (post models.WpPosts, err error) {
Id := id[0].(uint64)
posts, err := getPosts([]uint64{Id})
if err != nil {
return models.WpPosts{}, err
}
post = posts[Id]
return
}
func getPosts(ids ...any) (m map[uint64]models.WpPosts, err error) { func getPosts(ids ...any) (m map[uint64]models.WpPosts, err error) {
m = make(map[uint64]models.WpPosts) m = make(map[uint64]models.WpPosts)
id := ids[0].([]uint64) id := ids[0].([]uint64)

View File

@ -56,7 +56,7 @@ func Detail(c *gin.Context) {
} }
} }
ID := uint64(Id) ID := uint64(Id)
post, err := common.GetPostAndCache(c, ID, ID) post, err := common.GetPostAndCache(c, ID)
if post.Id == 0 || err != nil { if post.Id == 0 || err != nil {
return return
} }

View File

@ -168,12 +168,25 @@ func Index(c *gin.Context) {
"title": h.getTitle(), "title": h.getTitle(),
"recentComments": recentComments, "recentComments": recentComments,
} }
postIds, totalRaw, err := models.SimplePagination[models.WpPosts](h.where, "ID", "", h.page, h.pageSize, h.orderBy, h.join, h.postType, h.status) var postIds []models.WpPosts
var totalRaw int
var err error
if c.Param("month") != "" {
postIds, totalRaw, err = common.GetMonthPostIds(c, c.Param("year"), c.Param("month"), h.page, h.pageSize, h.order)
if err != nil {
return
}
} else {
postIds, totalRaw, err = models.SimplePagination[models.WpPosts](h.where, "ID", "", h.page, h.pageSize, h.orderBy, h.join, h.postType, h.status)
}
defer func() { defer func() {
c.HTML(http.StatusOK, "posts/index.gohtml", ginH) stat := http.StatusOK
if err != nil { if err != nil {
c.Error(err) c.Error(err)
stat = http.StatusInternalServerError
} }
c.HTML(stat, "posts/index.gohtml", ginH)
}() }()
if err != nil { if err != nil {
return return
@ -181,10 +194,7 @@ func Index(c *gin.Context) {
if len(postIds) < 1 && h.category != "" { if len(postIds) < 1 && h.category != "" {
h.titleL = "未找到页面" h.titleL = "未找到页面"
} }
err = common.SetPostCache(postIds)
if err != nil {
return
}
pw := h.session.Get("post_password") pw := h.session.Get("post_password")
plug := plugins.NewPostPlugin(c, h.scene) plug := plugins.NewPostPlugin(c, h.scene)
for i, v := range postIds { for i, v := range postIds {
@ -203,6 +213,9 @@ func Index(c *gin.Context) {
} }
} }
q := c.Request.URL.Query().Encode() q := c.Request.URL.Query().Encode()
if q != "" {
q = fmt.Sprintf("?%s", q)
}
ginH["posts"] = postIds ginH["posts"] = postIds
ginH["totalPage"] = h.getTotalPage(totalRaw) ginH["totalPage"] = h.getTotalPage(totalRaw)
ginH["pagination"] = pagination(h.page, h.totalPage, h.paginationStep, c.Request.URL.Path, q) ginH["pagination"] = pagination(h.page, h.totalPage, h.paginationStep, c.Request.URL.Path, q)

45
cache/map.go vendored
View File

@ -22,6 +22,14 @@ type mapCacheStruct[T any] struct {
data T data T
} }
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
m.setCacheFunc = fn
}
func (m *MapCache[K, V]) SetCacheBatchFunc(fn func(...any) (map[K]V, error)) {
m.setBatchCacheFn = fn
}
func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] { func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
return &MapCache[K, V]{ return &MapCache[K, V]{
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
@ -133,20 +141,35 @@ func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duratio
return data.data, err return data.data, err
} }
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
data, ok := m.data[key] var needFlush []K
var res []V
t := 0
for _, k := range key {
d, ok := m.data[k]
if !ok { if !ok {
data = mapCacheStruct[V]{} needFlush = append(needFlush, k)
continue
}
expired := time.Duration(d.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix())
if expired {
needFlush = append(needFlush, k)
}
t = t + d.incr
} }
var err error var err error
expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix())
//todo 这里应该判断下取出的值是否为零值,不过怎么操作呢? //todo 这里应该判断下取出的值是否为零值,不过怎么操作呢?
if !ok || (ok && m.expireTime >= 0 && expired) { if len(needFlush) > 0 {
t := data.incr
call := func() { call := func() {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
if data.incr > t { tt := 0
for _, dd := range needFlush {
if ddd, ok := m.data[dd]; ok {
tt = tt + ddd.incr
}
}
if tt > t {
return return
} }
r, er := m.setBatchCacheFn(params...) r, er := m.setBatchCacheFn(params...)
@ -157,7 +180,6 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Du
for k, v := range r { for k, v := range r {
m.set(k, v) m.set(k, v)
} }
data.data = m.data[key].data
} }
if timeout > 0 { if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout) ctx, cancel := context.WithTimeout(c, timeout)
@ -175,7 +197,10 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Du
} else { } else {
call() call()
} }
} }
return data.data, err for _, k := range key {
d := m.data[k]
res = append(res, d.data)
}
return res, err
} }

View File

@ -34,6 +34,9 @@ func StructColumn[T any, M any](arr []M, field string) (r []T) {
} }
func RangeSlice[T ~int | ~uint | ~int64 | ~int8 | ~int16 | ~int32 | ~uint64](start, end, step T) []T { func RangeSlice[T ~int | ~uint | ~int64 | ~int8 | ~int16 | ~int32 | ~uint64](start, end, step T) []T {
if step == 0 {
panic("step can't be 0")
}
l := int((end-start+1)/step + 1) l := int((end-start+1)/step + 1)
if l < 0 { if l < 0 {
l = 0 - l l = 0 - l
@ -218,3 +221,12 @@ func SliceReverse[T any](arr []T) []T {
} }
return r return r
} }
func SliceSelfReverse[T any](arr []T) []T {
l := len(arr)
half := l / 2
for i := 0; i < half; i++ {
arr[i], arr[l-i-1] = arr[l-i-1], arr[i]
}
return arr
}

View File

@ -473,3 +473,35 @@ func TestToInterface(t *testing.T) {
}) })
} }
} }
func TestSliceSelfReverse(t *testing.T) {
type args struct {
arr []int
}
tests := []struct {
name string
args args
want []int
}{
{
name: "t1",
args: args{
arr: RangeSlice(1, 10, 1),
},
want: RangeSlice(10, 1, -1),
}, {
name: "t2",
args: args{
arr: RangeSlice(1, 9, 1),
},
want: RangeSlice(9, 1, -1),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := SliceSelfReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) {
t.Errorf("SliceSelfReverse() = %v, want %v", got, tt.want)
}
})
}
}