optimize and expand map cache remove ver add dynamic set expired time

This commit is contained in:
xing 2023-10-30 21:52:15 +08:00
parent 86d1616732
commit 64a2c2e33b
11 changed files with 188 additions and 115 deletions

View File

@ -3,6 +3,7 @@ package cachemanager
import ( import (
"context" "context"
"errors" "errors"
"github.com/fthvgb1/wp-go/app/cmd/reload"
"github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
@ -16,6 +17,14 @@ var getSingleFn = safety.NewMap[string, func(context.Context, any, time.Duration
var getBatchFn = safety.NewMap[string, func(context.Context, any, time.Duration, ...any) (any, error)]() var getBatchFn = safety.NewMap[string, func(context.Context, any, time.Duration, ...any) (any, error)]()
var anyFlush = safety.NewMap[string, func()]() var anyFlush = safety.NewMap[string, func()]()
var expiredTime = safety.NewMap[string, expire]()
type expire struct {
fn func() time.Duration
p *safety.Var[time.Duration]
isUseManger *safety.Var[bool]
}
type flush interface { type flush interface {
Flush(ctx context.Context) Flush(ctx context.Context)
} }
@ -52,7 +61,7 @@ func FlushAnyVal(name ...string) {
} }
func pushFlushMap[K comparable, V any](m *cache.MapCache[K, V], args ...any) { func pushFlushMap[K comparable, V any](m *cache.MapCache[K, V], args ...any) {
name := parseArgs(args...) name, _ := parseArgs(args...)
if name != "" { if name != "" {
anyFlush.Store(name, func() { anyFlush.Store(name, func() {
m.Flush(ctx) m.Flush(ctx)
@ -108,15 +117,22 @@ func GetMultiple[T, K any](name string, ct context.Context, key []K, timeout tim
return return
} }
func parseArgs(args ...any) string { func parseArgs(args ...any) (string, func() time.Duration) {
var name string var name string
var fn func() time.Duration
for _, arg := range args { for _, arg := range args {
v, ok := arg.(string) v, ok := arg.(string)
if ok { if ok {
name = v name = v
continue
} }
vv, ok := arg.(func() time.Duration)
if ok {
fn = vv
}
} }
return name return name, fn
} }
func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] { func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapBatchFn[K, V], fn cache.MapSingleFn[K, V], args ...any) *cache.MapCache[K, V] {
@ -128,7 +144,48 @@ func NewMapCache[K comparable, V any](data cache.Cache[K, V], batchFn cache.MapB
} }
func NewMemoryMapCache[K comparable, V any](batchFn cache.MapBatchFn[K, V], func NewMemoryMapCache[K comparable, V any](batchFn cache.MapBatchFn[K, V],
fn cache.MapSingleFn[K, V], expireTime time.Duration, args ...any) *cache.MapCache[K, V] { fn cache.MapSingleFn[K, V], expireTime time.Duration, args ...any) *cache.MapCache[K, V] {
return NewMapCache[K, V](cache.NewMemoryMapCache[K, V](expireTime), batchFn, fn, args...) name, f := parseArgs(args...)
var t, tt func() time.Duration
t = f
if t == nil {
t = func() time.Duration {
return expireTime
}
}
tt = t
if name != "" {
expireTime = t()
p := safety.NewVar(expireTime)
e := expire{
fn: t,
p: p,
isUseManger: safety.NewVar(false),
}
expiredTime.Store(name, e)
reload.Push(func() {
if !e.isUseManger.Load() {
e.p.Store(tt())
}
}, str.Join("cacheManger-", name, "-expiredTime"))
t = func() time.Duration {
return e.p.Load()
}
}
return NewMapCache[K, V](cache.NewMemoryMapCache[K, V](t), batchFn, fn, args...)
}
func SetExpireTime(t time.Duration, name ...string) {
for _, s := range name {
v, ok := expiredTime.Load(s)
if !ok {
continue
}
v.p.Store(t)
if !v.isUseManger.Load() {
v.isUseManger.Store(true)
}
}
} }
func FlushPush(f ...flush) { func FlushPush(f ...flush) {

View File

@ -13,7 +13,7 @@ type queue struct {
name string name string
} }
var calls []queue var calls = safety.NewSlice(make([]queue, 0))
var callsM = safety.NewMap[string, func()]() var callsM = safety.NewMap[string, func()]()
var anyMap = safety.NewMap[string, any]() var anyMap = safety.NewMap[string, any]()
@ -252,7 +252,8 @@ func SafeMap[K comparable, T any](args ...any) *safety.Map[K, T] {
func Push(fn func(), a ...any) { func Push(fn func(), a ...any) {
ord, name := parseArgs(a...) ord, name := parseArgs(a...)
calls = append(calls, queue{fn, ord, name}) calls.Append(queue{fn, ord, name})
//calls = append(calls, queue{fn, ord, name})
if name != "" { if name != "" {
callsM.Store(name, fn) callsM.Store(name, fn)
} }
@ -263,10 +264,10 @@ func Reload() {
safetyMaps.Flush() safetyMaps.Flush()
callsM.Flush() callsM.Flush()
flushMapFn.Flush() flushMapFn.Flush()
slice.Sort(calls, func(i, j queue) bool { slice.Sort(calls.Load(), func(i, j queue) bool {
return i.order > j.order return i.order > j.order
}) })
for _, call := range calls { for _, call := range calls.Load() {
call.fn() call.fn()
} }
return return

View File

@ -41,33 +41,55 @@ var allUsernameCache *cache.VarCache[map[string]struct{}]
func InitActionsCommonCache() { func InitActionsCommonCache() {
c := config.GetConfig() c := config.GetConfig()
searchPostIdsCache = cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds") searchPostIdsCache = cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.SearchPostCacheTime, "searchPostIds", func() time.Duration {
return config.GetConfig().CacheTime.SearchPostCacheTime
})
postListIdsCache = cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds") postListIdsCache = cachemanager.NewMemoryMapCache(nil, dao.SearchPostIds, c.CacheTime.PostListCacheTime, "listPostIds", func() time.Duration {
return config.GetConfig().CacheTime.PostListCacheTime
})
monthPostsCache = cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds") monthPostsCache = cachemanager.NewMemoryMapCache(nil, dao.MonthPost, c.CacheTime.MonthPostCacheTime, "monthPostIds", func() time.Duration {
return config.GetConfig().CacheTime.MonthPostCacheTime
})
postContextCache = cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext") postContextCache = cachemanager.NewMemoryMapCache(nil, dao.GetPostContext, c.CacheTime.ContextPostCacheTime, "postContext", func() time.Duration {
return config.GetConfig().CacheTime.ContextPostCacheTime
})
postsCache = cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData") postsCache = cachemanager.NewMemoryMapCache(dao.GetPostsByIds, nil, c.CacheTime.PostDataCacheTime, "postData", func() time.Duration {
return config.GetConfig().CacheTime.PostDataCacheTime
})
postMetaCache = cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData") postMetaCache = cachemanager.NewMemoryMapCache(dao.GetPostMetaByPostIds, nil, c.CacheTime.PostDataCacheTime, "postMetaData", func() time.Duration {
return config.GetConfig().CacheTime.PostDataCacheTime
})
categoryAndTagsCaches = cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData") categoryAndTagsCaches = cachemanager.NewMemoryMapCache(nil, dao.CategoriesAndTags, c.CacheTime.CategoryCacheTime, "categoryAndTagsData", func() time.Duration {
return config.GetConfig().CacheTime.CategoryCacheTime
})
recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime) recentPostsCaches = cache.NewVarCache(dao.RecentPosts, c.CacheTime.RecentPostCacheTime)
recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime) recentCommentsCaches = cache.NewVarCache(dao.RecentComments, c.CacheTime.RecentCommentsCacheTime)
postCommentCaches = cachemanager.NewMemoryMapCache(nil, dao.PostComments, c.CacheTime.PostCommentsCacheTime, "postCommentIds") postCommentCaches = cachemanager.NewMemoryMapCache(nil, dao.PostComments, c.CacheTime.PostCommentsCacheTime, "postCommentIds", func() time.Duration {
return config.GetConfig().CacheTime.PostCommentsCacheTime
})
maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime) maxPostIdCache = cache.NewVarCache(dao.GetMaxPostId, c.CacheTime.MaxPostIdCacheTime)
cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData") cachemanager.NewMemoryMapCache(nil, dao.GetUserById, c.CacheTime.UserInfoCacheTime, "userData", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime
})
usersNameCache = cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameMapToUserData") usersNameCache = cachemanager.NewMemoryMapCache(nil, dao.GetUserByName, c.CacheTime.UserInfoCacheTime, "usernameMapToUserData", func() time.Duration {
return config.GetConfig().CacheTime.UserInfoCacheTime
})
cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, c.CacheTime.CommentsCacheTime, "commentData") cachemanager.NewMemoryMapCache(dao.GetCommentByIds, nil, c.CacheTime.CommentsCacheTime, "commentData", func() time.Duration {
return config.GetConfig().CacheTime.CommentsCacheTime
})
allUsernameCache = cache.NewVarCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime) allUsernameCache = cache.NewVarCache(dao.AllUsername, c.CacheTime.UserInfoCacheTime)

View File

@ -22,7 +22,9 @@ var more = regexp.MustCompile("<!--more(.*?)?-->")
var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->") var removeWpBlock = regexp.MustCompile("<!-- /?wp:.*-->")
func InitDigestCache() { func InitDigestCache() {
digestCache = cachemanager.NewMemoryMapCache(nil, digestRaw, config.GetConfig().CacheTime.DigestCacheTime, "digestPlugin") digestCache = cachemanager.NewMemoryMapCache(nil, digestRaw, config.GetConfig().CacheTime.DigestCacheTime, "digestPlugin", func() time.Duration {
return config.GetConfig().CacheTime.DigestCacheTime
})
} }
func RemoveWpBlock(s string) string { func RemoveWpBlock(s string) string {

View File

@ -2,13 +2,14 @@ package wp
import ( import (
"errors" "errors"
"github.com/fthvgb1/wp-go/safety"
) )
var fnMap map[string]map[string]any var fnMap = safety.NewMap[string, map[string]any]() //map[string]map[string]any
var fnHook map[string]map[string]any var fnHook = safety.NewMap[string, map[string]any]() // map[string]map[string]any
func GetFn[T any](fnType string, name string) []T { func GetFn[T any](fnType string, name string) []T {
v, ok := fnMap[fnType] v, ok := fnMap.Load(fnType)
if !ok { if !ok {
return nil return nil
} }
@ -19,7 +20,7 @@ func GetFn[T any](fnType string, name string) []T {
return vv.([]T) return vv.([]T)
} }
func GetFnHook[T any](fnType string, name string) []T { func GetFnHook[T any](fnType string, name string) []T {
v, ok := fnHook[fnType] v, ok := fnHook.Load(fnType)
if !ok { if !ok {
return nil return nil
} }
@ -31,10 +32,10 @@ func GetFnHook[T any](fnType string, name string) []T {
} }
func PushFn[T any](fnType string, name string, fns ...T) error { func PushFn[T any](fnType string, name string, fns ...T) error {
v, ok := fnMap[fnType] v, ok := fnMap.Load(fnType)
if !ok { if !ok {
v = make(map[string]any) v = make(map[string]any)
fnMap[fnType] = v fnMap.Store(fnType, v)
v[name] = fns v[name] = fns
return nil return nil
} }
@ -52,10 +53,10 @@ func PushFn[T any](fnType string, name string, fns ...T) error {
} }
func PushFnHook[T any](fnType string, name string, fns ...T) error { func PushFnHook[T any](fnType string, name string, fns ...T) error {
v, ok := fnHook[fnType] v, ok := fnHook.Load(fnType)
if !ok { if !ok {
v = make(map[string]any) v = make(map[string]any)
fnHook[fnType] = v fnHook.Store(fnType, v)
v[name] = fns v[name] = fns
return nil return nil
} }

View File

@ -107,8 +107,8 @@ func InitHandle(fn func(*Handle), h *Handle) {
h.handlers = make(map[string]map[string][]HandleCall) h.handlers = make(map[string]map[string][]HandleCall)
h.handleHook = make(map[string][]func(HandleCall) (HandleCall, bool)) h.handleHook = make(map[string][]func(HandleCall) (HandleCall, bool))
h.ginH = gin.H{} h.ginH = gin.H{}
fnMap = map[string]map[string]any{} fnMap.Flush()
fnHook = map[string]map[string]any{} fnHook.Flush()
fn(h) fn(h)
v := apply.UsePlugins() v := apply.UsePlugins()
pluginFn, ok := v.(func(*Handle)) pluginFn, ok := v.(func(*Handle))

2
cache/cache.go vendored
View File

@ -10,7 +10,6 @@ type Cache[K comparable, V any] interface {
Set(ctx context.Context, key K, val V) Set(ctx context.Context, key K, val V)
GetExpireTime(ctx context.Context) time.Duration GetExpireTime(ctx context.Context) time.Duration
Ttl(ctx context.Context, key K) time.Duration Ttl(ctx context.Context, key K) time.Duration
Ver(ctx context.Context, key K) int
Flush(ctx context.Context) Flush(ctx context.Context)
Del(ctx context.Context, key ...K) Del(ctx context.Context, key ...K)
ClearExpired(ctx context.Context) ClearExpired(ctx context.Context)
@ -18,6 +17,5 @@ type Cache[K comparable, V any] interface {
type Expend[K comparable, V any] interface { type Expend[K comparable, V any] interface {
Gets(ctx context.Context, k []K) (map[K]V, error) Gets(ctx context.Context, k []K) (map[K]V, error)
Vers(ctx context.Context, k []K) map[K]int
Sets(ctx context.Context, m map[K]V) Sets(ctx context.Context, m map[K]V)
} }

116
cache/map.go vendored
View File

@ -98,39 +98,39 @@ func (m *MapCache[K, V]) Flush(ctx context.Context) {
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) { func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
data, ok := m.Get(c, key) data, ok := m.Get(c, key)
if ok {
return data, nil
}
var err error var err error
if !ok { call := func() {
ver := m.Ver(c, key) m.mux.Lock()
call := func() { defer m.mux.Unlock()
m.mux.Lock() if data, ok = m.Get(c, key); ok {
defer m.mux.Unlock() return
if m.Ver(c, key) > ver {
data, _ = m.Get(c, key)
return
}
data, err = m.cacheFunc(c, key, params...)
if err != nil {
return
}
m.Set(c, key, data)
} }
if timeout > 0 { data, err = m.cacheFunc(c, key, params...)
ctx, cancel := context.WithTimeout(c, timeout) if err != nil {
defer cancel() return
done := make(chan struct{}, 1) }
go func() { m.Set(c, key, data)
call() }
done <- struct{}{} if timeout > 0 {
}() ctx, cancel := context.WithTimeout(c, timeout)
select { defer cancel()
case <-ctx.Done(): done := make(chan struct{}, 1)
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error())) go func() {
case <-done:
}
} else {
call() call()
done <- struct{}{}
}()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
var vv V
return vv, err
case <-done:
} }
} else {
call()
} }
return data, err return data, err
} }
@ -142,11 +142,9 @@ func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.
func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) { func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
var res = make([]V, 0, len(key)) var res = make([]V, 0, len(key))
var needIndex = make(map[K]int) var needIndex = make(map[K]int)
var ver = make(map[K]int)
for i, k := range key { for i, k := range key {
v, ok := m.Get(c, k) v, ok := m.Get(c, k)
if !ok { if !ok {
ver[k] = m.Ver(c, k)
needIndex[k] = i needIndex[k] = i
} }
res = append(res, v) res = append(res, v)
@ -160,13 +158,16 @@ func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time
m.mux.Lock() m.mux.Lock()
defer m.mux.Unlock() defer m.mux.Unlock()
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) { needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, ver[k] >= m.Ver(c, k) vv, ok := m.Get(c, k)
if ok {
res[needIndex[k]] = vv
delete(needIndex, k)
return k, false
}
return k, true
}) })
if len(needFlushs) < 1 { if len(needFlushs) < 1 {
for k, i := range needIndex {
res[i], _ = m.Get(c, k)
}
return return
} }
@ -180,11 +181,6 @@ func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time
if ok { if ok {
res[i] = v res[i] = v
m.Set(c, k, v) m.Set(c, k, v)
} else {
v, ok = m.Get(c, k)
if ok {
res[i] = v
}
} }
} }
} }
@ -199,6 +195,7 @@ func (m *MapCache[K, V]) getCacheBatchs(c context.Context, key []K, timeout time
select { select {
case <-ctx.Done(): case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error())) err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done: case <-done:
} }
} else { } else {
@ -232,37 +229,29 @@ func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, ke
if len(needIndex) < 1 { if len(needIndex) < 1 {
return res, nil return res, nil
} }
vers := cc.Vers(ctx, flushKeys)
call := func() { call := func() {
m.mux.Lock() m.mux.Lock()
defer m.mux.Unlock() defer m.mux.Unlock()
verss := cc.Vers(ctx, flushKeys) mmm, er := cc.Gets(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
needFlushs := maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) { return k, true
vv, ok := vers[k] }))
vvv, ook := verss[k] if er != nil {
if !ok || !ook || vv >= vvv { err = er
return k, true return
} }
return k, false for k, v := range mmm {
}) res[needIndex[k]] = v
delete(needIndex, k)
}
if len(needFlushs) < 1 { if len(needIndex) < 1 {
vv, er := cc.Gets(ctx, needFlushs)
if er != nil {
err = er
return
}
for k, i := range needIndex {
v, ok := vv[k]
if ok {
res[i] = v
}
}
return return
} }
r, er := m.batchCacheFn(ctx, needFlushs, params...) r, er := m.batchCacheFn(ctx, maps.FilterToSlice(needIndex, func(k K, v int) (K, bool) {
return k, true
}), params...)
if err != nil { if err != nil {
err = er err = er
return return
@ -287,6 +276,7 @@ func (m *MapCache[K, V]) getBatches(e Expend[K, V]) func(ctx context.Context, ke
select { select {
case <-ctx.Done(): case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error())) err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
return nil, err
case <-done: case <-done:
} }
} else { } else {

9
cache/map_test.go vendored
View File

@ -27,10 +27,7 @@ func init() {
return t, strings.Repeat(t, 2), true return t, strings.Repeat(t, 2), true
}), nil }), nil
} }
ca = *NewMemoryMapCacheByFn[string, string](fn, time.Second*2)
ca.SetCacheBatchFn(batchFn)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
_, _ = ca.GetCache(ct, "bb", time.Second, ct, "bb")
} }
func TestMapCache_ClearExpired(t *testing.T) { func TestMapCache_ClearExpired(t *testing.T) {
type args struct { type args struct {
@ -70,7 +67,9 @@ func TestMapCache_Flush(t *testing.T) {
m MapCache[K, V] m MapCache[K, V]
args args args args
} }
ca := *NewMemoryMapCacheByFn[string, string](fn, time.Second) ca := *NewMapCache[string, string](NewMemoryMapCache[string, string](func() time.Duration {
return time.Second
}), fn, nil)
_, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa") _, _ = ca.GetCache(ct, "aa", time.Second, ct, "aa")
tests := []testCase[string, string]{ tests := []testCase[string, string]{
{ {

View File

@ -3,24 +3,15 @@ package cache
import ( import (
"context" "context"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"sync"
"time" "time"
) )
type MemoryMapCache[K comparable, V any] struct { type MemoryMapCache[K comparable, V any] struct {
*safety.Map[K, mapVal[V]] *safety.Map[K, mapVal[V]]
expireTime time.Duration expireTime func() time.Duration
} }
func NewMemoryMapCacheByFn[K comparable, V any](fn MapSingleFn[K, V], expireTime time.Duration) *MapCache[K, V] { func NewMemoryMapCache[K comparable, V any](expireTime func() time.Duration) *MemoryMapCache[K, V] {
return &MapCache[K, V]{
Cache: NewMemoryMapCache[K, V](expireTime),
cacheFunc: fn,
mux: sync.Mutex{},
}
}
func NewMemoryMapCache[K comparable, V any](expireTime time.Duration) *MemoryMapCache[K, V] {
return &MemoryMapCache[K, V]{ return &MemoryMapCache[K, V]{
Map: safety.NewMap[K, mapVal[V]](), Map: safety.NewMap[K, mapVal[V]](),
expireTime: expireTime, expireTime: expireTime,
@ -34,7 +25,7 @@ type mapVal[T any] struct {
} }
func (m *MemoryMapCache[K, V]) GetExpireTime(_ context.Context) time.Duration { func (m *MemoryMapCache[K, V]) GetExpireTime(_ context.Context) time.Duration {
return m.expireTime return m.expireTime()
} }
func (m *MemoryMapCache[K, V]) Get(_ context.Context, key K) (r V, ok bool) { func (m *MemoryMapCache[K, V]) Get(_ context.Context, key K) (r V, ok bool) {
@ -43,7 +34,7 @@ func (m *MemoryMapCache[K, V]) Get(_ context.Context, key K) (r V, ok bool) {
return return
} }
r = v.data r = v.data
t := m.expireTime - time.Now().Sub(v.setTime) t := m.expireTime() - time.Now().Sub(v.setTime)
if t <= 0 { if t <= 0 {
ok = false ok = false
} }
@ -72,7 +63,7 @@ func (m *MemoryMapCache[K, V]) Ttl(_ context.Context, key K) time.Duration {
if !ok { if !ok {
return time.Duration(-1) return time.Duration(-1)
} }
return m.expireTime - time.Now().Sub(v.setTime) return m.expireTime() - time.Now().Sub(v.setTime)
} }
func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int { func (m *MemoryMapCache[K, V]) Ver(_ context.Context, key K) int {
@ -96,7 +87,7 @@ func (m *MemoryMapCache[K, V]) Del(_ context.Context, keys ...K) {
func (m *MemoryMapCache[K, V]) ClearExpired(_ context.Context) { func (m *MemoryMapCache[K, V]) ClearExpired(_ context.Context) {
now := time.Duration(time.Now().UnixNano()) now := time.Duration(time.Now().UnixNano())
m.Range(func(k K, v mapVal[V]) bool { m.Range(func(k K, v mapVal[V]) bool {
if now > time.Duration(v.setTime.UnixNano())+m.expireTime { if now > time.Duration(v.setTime.UnixNano())+m.expireTime() {
m.Map.Delete(k) m.Map.Delete(k)
} }
return true return true

View File

@ -24,6 +24,18 @@ func StructToAnyMap[K comparable, T any](s T) (r map[K]any, err error) {
return return
} }
func Filter[M ~map[K]V, K comparable, V any](fn func(K, V) bool, m ...M) M {
var r = map[K]V{}
for _, mm := range m {
for k, v := range mm {
if ok := fn(k, v); ok {
r[k] = v
}
}
}
return r
}
func FilterToSlice[T any, K comparable, V any](m map[K]V, fn func(K, V) (T, bool)) (r []T) { func FilterToSlice[T any, K comparable, V any](m map[K]V, fn func(K, V) (T, bool)) (r []T) {
for k, v := range m { for k, v := range m {
vv, ok := fn(k, v) vv, ok := fn(k, v)