diff --git a/actions/common/common.go b/actions/common/common.go index c39dca3..dba736a 100644 --- a/actions/common/common.go +++ b/actions/common/common.go @@ -20,18 +20,21 @@ var PostContextCache sync.Map var archivesCaches *Arch var categoryCaches *cache.SliceCache[models.WpTermsMy] var recentPostsCaches *cache.SliceCache[models.WpPosts] -var monthCaches *cache.MapCache[string, []models.WpPosts] var recentCommentsCaches *cache.SliceCache[models.WpComments] var postCommentCaches *cache.MapCache[uint64, []models.WpComments] +var postsCache *cache.MapCache[uint64, models.WpPosts] -func InitCache() { +func InitActionsCommonCache() { archivesCaches = &Arch{ mutex: &sync.Mutex{}, setCacheFunc: archives, } + postsCache = cache.NewMapBatchCache[uint64, models.WpPosts](getPosts, time.Hour) + categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime) + recentPostsCaches = cache.NewSliceCache[models.WpPosts](recentPosts, vars.Conf.RecentPostCacheTime) - monthCaches = cache.NewMapCache[string, []models.WpPosts](getMonthPost, 30*time.Minute) + recentCommentsCaches = cache.NewSliceCache[models.WpComments](recentComments, vars.Conf.RecentCommentsCacheTime) postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5) } @@ -43,7 +46,7 @@ type Arch struct { month time.Month } -func (c *Arch) GetCache() []models.PostArchive { +func (c *Arch) getArchiveCache() []models.PostArchive { l := len(c.data) m := time.Now().Month() if l > 0 && c.month != m || l < 1 { @@ -67,10 +70,6 @@ type PostContext struct { setTime time.Time } -func GetMonthPost(ctx context.Context, year, month string) ([]models.WpPosts, error) { - return monthCaches.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month) -} - func PostComments(ctx context.Context, Id uint64) ([]models.WpComments, error) { return postCommentCaches.GetCache(ctx, Id, time.Second, Id) } @@ -151,14 +150,6 @@ func getPostContext(t time.Time) (prev, next models.WpPosts, err error) { return } -func GetPostFromCache(Id uint64) (r models.WpPosts) { - p, ok := PostsCache.Load(Id) - if ok { - r = *p.(*models.WpPosts) - } - return -} - func QueryAndSetPostCache(postIds []models.WpPosts) (err error) { var all []uint64 var needQuery []any @@ -223,7 +214,7 @@ func archives() ([]models.PostArchive, error) { } func Archives() (r []models.PostArchive) { - return archivesCaches.GetCache() + return archivesCaches.getArchiveCache() } func Categories(ctx context.Context) []models.WpTermsMy { diff --git a/actions/common/posts.go b/actions/common/posts.go new file mode 100644 index 0000000..21d15c8 --- /dev/null +++ b/actions/common/posts.go @@ -0,0 +1,34 @@ +package common + +import ( + "context" + "github/fthvgb1/wp-go/helper" + "github/fthvgb1/wp-go/models" + "time" +) + +func GetPostById(ctx context.Context, id uint64, ids ...uint64) (models.WpPosts, error) { + + return postsCache.GetCacheBatch(ctx, id, time.Second, ids) +} + +func getPosts(ids ...any) (m map[uint64]models.WpPosts, err error) { + m = make(map[uint64]models.WpPosts) + id := ids[0].([]uint64) + arg := helper.SliceMap(id, helper.ToAny[uint64]) + rawPosts, err := models.Find[models.WpPosts](models.SqlBuilder{{ + "Id", "in", "", + }}, "a.*,ifnull(d.name,'') category_name,ifnull(taxonomy,'') `taxonomy`", "", nil, models.SqlBuilder{{ + "a", "left join", "wp_term_relationships b", "a.Id=b.object_id", + }, { + "left join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id", + }, { + "left join", "wp_terms d", "c.term_id=d.term_id", + }}, 0, arg) + if err == nil { + for _, v := range rawPosts { + m[v.Id] = v + } + } + return +} diff --git a/actions/detail.go b/actions/detail.go index 36969b0..6c66e25 100644 --- a/actions/detail.go +++ b/actions/detail.go @@ -55,17 +55,9 @@ func Detail(c *gin.Context) { } } ID := uint64(Id) - post := common.GetPostFromCache(ID) - if post.Id == 0 { - er := common.QueryAndSetPostCache([]models.WpPosts{{Id: ID}}) - if er != nil { - err = er - return - } - post = common.GetPostFromCache(ID) - if post.Id == 0 { - return - } + post, err := common.GetPostById(c, ID, ID) + if post.Id == 0 || err != nil { + return } pw := sessions.Default(c).Get("post_password") showComment := false diff --git a/cache/map.go b/cache/map.go index 5c5f481..a7110a0 100644 --- a/cache/map.go +++ b/cache/map.go @@ -9,12 +9,17 @@ import ( ) type MapCache[K comparable, V any] struct { - data map[K]V - mutex *sync.Mutex - setCacheFunc func(...any) (V, error) - expireTime time.Duration - setTime time.Time - incr int + data map[K]mapCacheStruct[V] + mutex *sync.Mutex + setCacheFunc func(...any) (V, error) + setBatchCacheFn func(...any) (map[K]V, error) + expireTime time.Duration +} + +type mapCacheStruct[T any] struct { + setTime time.Time + incr int + data T } func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] { @@ -22,41 +27,79 @@ func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime ti mutex: &sync.Mutex{}, setCacheFunc: fun, expireTime: expireTime, - data: make(map[K]V), + data: make(map[K]mapCacheStruct[V]), + } +} +func NewMapBatchCache[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] { + return &MapCache[K, V]{ + mutex: &sync.Mutex{}, + setBatchCacheFn: fn, + expireTime: expireTime, + data: make(map[K]mapCacheStruct[V]), } } -func (c *MapCache[K, V]) FlushCache(k any) { - c.mutex.Lock() - defer c.mutex.Unlock() +func (m *MapCache[K, V]) FlushCache(k any) { + m.mutex.Lock() + defer m.mutex.Unlock() key := k.(K) - delete(c.data, key) + delete(m.data, key) } -func (c *MapCache[K, V]) GetCache(ctx context.Context, key K, timeout time.Duration, params ...any) (V, error) { - _, ok := c.data[key] +func (m *MapCache[K, V]) Get(k K) V { + return m.data[k].data +} + +func (m *MapCache[K, V]) Set(k K, v V) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.set(k, v) +} + +func (m *MapCache[K, V]) set(k K, v V) { + data, ok := m.data[k] + t := time.Now() + if !ok { + data.data = v + data.setTime = t + data.incr++ + m.data[k] = data + } else { + m.data[k] = mapCacheStruct[V]{ + data: v, + setTime: t, + } + } +} + +func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { + data, ok := m.data[key] + if !ok { + data = mapCacheStruct[V]{} + } var err error - expired := time.Duration(c.setTime.Unix())+c.expireTime/time.Second < time.Duration(time.Now().Unix()) + expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix()) //todo 这里应该判断下取出的值是否为零值,不过怎么操作呢? - if !ok || (c.expireTime >= 0 && expired) { - t := c.incr + if !ok || (ok && m.expireTime >= 0 && expired) { + t := data.incr call := func() { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.incr > t { + m.mutex.Lock() + defer m.mutex.Unlock() + if data.incr > t { return } - r, er := c.setCacheFunc(params...) + r, er := m.setCacheFunc(params...) if err != nil { err = er return } - c.setTime = time.Now() - c.data[key] = r - c.incr++ + data.setTime = time.Now() + data.data = r + m.data[key] = data + data.incr++ } if timeout > 0 { - ctx, cancel := context.WithTimeout(ctx, timeout) + ctx, cancel := context.WithTimeout(c, timeout) defer cancel() done := make(chan struct{}) go func() { @@ -73,5 +116,52 @@ func (c *MapCache[K, V]) GetCache(ctx context.Context, key K, timeout time.Durat } } - return c.data[key], err + return data.data, err +} + +func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { + data, ok := m.data[key] + if !ok { + data = mapCacheStruct[V]{} + } + var err error + expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix()) + //todo 这里应该判断下取出的值是否为零值,不过怎么操作呢? + if !ok || (ok && m.expireTime >= 0 && expired) { + t := data.incr + call := func() { + m.mutex.Lock() + defer m.mutex.Unlock() + if data.incr > t { + return + } + r, er := m.setBatchCacheFn(params...) + if err != nil { + err = er + return + } + for k, v := range r { + m.set(k, v) + } + data.data = m.data[key].data + } + if timeout > 0 { + ctx, cancel := context.WithTimeout(c, timeout) + defer cancel() + done := make(chan struct{}) + go func() { + call() + done <- struct{}{} + }() + select { + case <-ctx.Done(): + err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error())) + case <-done: + } + } else { + call() + } + + } + return data.data, err } diff --git a/helper/func.go b/helper/func.go index 8a7d4f8..3ec8a6e 100644 --- a/helper/func.go +++ b/helper/func.go @@ -10,6 +10,10 @@ import ( "strings" ) +func ToAny[T any](v T) any { + return v +} + func IsContainInArr[T comparable](a T, arr []T) bool { for _, v := range arr { if a == v { diff --git a/helper/func_test.go b/helper/func_test.go index fc7c03d..1071998 100644 --- a/helper/func_test.go +++ b/helper/func_test.go @@ -449,3 +449,27 @@ func TestSliceReverse(t *testing.T) { }) } } + +func TestToInterface(t *testing.T) { + type args struct { + v int + } + tests := []struct { + name string + args args + want any + }{ + { + name: "t1", + args: args{v: 1}, + want: any(1), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToAny(tt.args.v); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToAny() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/main.go b/main.go index a48b013..43dab4b 100644 --- a/main.go +++ b/main.go @@ -30,8 +30,8 @@ func init() { panic(err) } - common.InitCache() - plugins.InitDigest() + common.InitActionsCommonCache() + plugins.InitDigestCache() } func main() { diff --git a/plugins/digest.go b/plugins/digest.go index 50efc07..b05fb9e 100644 --- a/plugins/digest.go +++ b/plugins/digest.go @@ -18,8 +18,8 @@ var more = regexp.MustCompile("") var digestCache *cache.MapCache[uint64, string] var quto = regexp.MustCompile(`" *|& *|< *|> ?|  *`) -func InitDigest() { - digestCache = cache.NewMapCache[uint64](digestRaw, time.Second) +func InitDigestCache() { + digestCache = cache.NewMapCache[uint64](digestRaw, vars.Conf.DigestCacheTime) } func digestRaw(arg ...any) (string, error) { @@ -108,6 +108,5 @@ func Digest(p *Plugin[models.WpPosts], c *gin.Context, post *models.WpPosts, sce if scene == Detail { return } - //post.PostContent = DigestCache(c, post.Id, post.PostContent) - post.PostContent = DigestRaw(post.PostContent, vars.Conf.DigestWordCount, post.Id) + post.PostContent = DigestCache(c, post.Id, post.PostContent) }