From 5eaf798a6ccfd05a866d042426d1c28b0bcff64e Mon Sep 17 00:00:00 2001 From: xing Date: Mon, 15 Apr 2024 15:25:19 +0800 Subject: [PATCH] optimize reload schema and optimize cachemanager package --- .../devexample/plugintt/redisCache.go.dev | 32 +- app/theme/wp/wp.go | 8 +- cache/cachemanager/manger.go | 339 +++++------------- cache/cachemanager/manger_test.go | 4 +- cache/cachemanager/mapcache.go | 147 ++++++++ cache/cachemanager/pagination.go | 57 +++ cache/cachemanager/varcache.go | 82 +++++ helper/slice/slice.go | 9 + 8 files changed, 416 insertions(+), 262 deletions(-) create mode 100644 cache/cachemanager/mapcache.go create mode 100644 cache/cachemanager/pagination.go create mode 100644 cache/cachemanager/varcache.go diff --git a/app/plugins/devexample/plugintt/redisCache.go.dev b/app/plugins/devexample/plugintt/redisCache.go.dev index 9e02a71..224f032 100644 --- a/app/plugins/devexample/plugintt/redisCache.go.dev +++ b/app/plugins/devexample/plugintt/redisCache.go.dev @@ -6,6 +6,7 @@ import ( "fmt" "github.com/fthvgb1/wp-go/app/pkg/config" "github.com/fthvgb1/wp-go/app/pkg/dao" + "github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/cache/cachemanager" "github.com/fthvgb1/wp-go/cache/reload" @@ -14,7 +15,7 @@ import ( "github.com/fthvgb1/wp-go/helper/strings" "github.com/redis/go-redis/v9" "strconv" - strings2 "strings" + str "strings" "time" ) @@ -58,7 +59,6 @@ func (r *RdmCache[K, V]) Set(ctx context.Context, key K, val V) { return } fmt.Println(result, err) - } func (r *RdmCache[K, V]) GetExpireTime(ctx context.Context) time.Duration { @@ -74,7 +74,7 @@ func (r *RdmCache[K, V]) Ttl(ctx context.Context, key K) time.Duration { } func (r *RdmCache[K, V]) Flush(ctx context.Context) { - + fmt.Println("flush redis cache") } func (r *RdmCache[K, V]) Del(ctx context.Context, key ...K) { @@ -82,12 +82,13 @@ func (r *RdmCache[K, V]) Del(ctx context.Context, key ...K) { } func (r *RdmCache[K, V]) ClearExpired(ctx context.Context) { - + fmt.Println("clear expired redis cache") } -// use step: -//1 go build -gcflags all="-N -l" --race -buildmode=plugin -o redisCache.so main.go && cp ./redisCache.so wp-go/plugins/ -//2 wp-go config add redisCache plugin -func Re(h *wp.Handle) { + +// RedisCache use step: +// 1 go build -gcflags all="-N -l" --race -buildmode=plugin -o redisCache.so main.go && cp ./redisCache.so ../wp-go/plugins/ +// 2 wp-go config add redisCache plugin +func RedisCache(h *wp.Handle) { vv, ok := cachemanager.GetMapCache[string, dao.PostIds]("listPostIds") if ok { _, ok := any(vv.Cache).(*RdmCache[string, dao.PostIds]) @@ -96,8 +97,15 @@ func Re(h *wp.Handle) { } } reload.AppendOnceFn(func() { - cachemanager.SetMapCache("listPostIds",vv) - }) + err := cachemanager.SetMapCache("listPostIds", vv) + if err != nil { + logs.Error(err, "set recovery listPostIds cache err") + } else { + cachemanager.PushOrSetFlush(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush}) + cachemanager.PushOrSetClearExpired(cachemanager.Queue{Name: "listPostIds", Fn: vv.Flush}) + fmt.Println("recovery listPostIds cache ok") + } + }) rdm := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set @@ -114,14 +122,14 @@ func Re(h *wp.Handle) { name: "", resFn: func(m map[string]string) dao.PostIds { return dao.PostIds{ - Ids: slice.Map(strings2.Split(m["ids"], ","), strings.ToInt[uint64]), + Ids: slice.Map(str.Split(m["ids"], ","), strings.ToInt[uint64]), Length: strings.ToInt[int](m["length"]), } }, saveData: func(ids dao.PostIds) map[string]string { t := slice.Map(ids.Ids, number.IntToString[uint64]) return map[string]string{ - "ids": strings2.Join(t, ","), + "ids": str.Join(t, ","), "length": strconv.Itoa(ids.Length), } }, diff --git a/app/theme/wp/wp.go b/app/theme/wp/wp.go index 2ffcf24..c7eca6b 100644 --- a/app/theme/wp/wp.go +++ b/app/theme/wp/wp.go @@ -87,6 +87,8 @@ type HandleCall struct { Name string } +var isFirstRequest = true + func SetConfigHandle(a ...any) Handle { configFn := a[0].(func(*Handle)) hh := a[1].(*Handle) @@ -100,6 +102,11 @@ func SetConfigHandle(a ...any) Handle { h.ginH = gin.H{} fnMap.Flush() fnHook.Flush() + if isFirstRequest { + isFirstRequest = false + } else { + reload.Reload() + } h.C = hh.C h.theme = hh.theme configFn(h) @@ -108,7 +115,6 @@ func SetConfigHandle(a ...any) Handle { if ok { pluginFn(h) } - reload.Reload() return *h } diff --git a/cache/cachemanager/manger.go b/cache/cachemanager/manger.go index b8612eb..68b5d2a 100644 --- a/cache/cachemanager/manger.go +++ b/cache/cachemanager/manger.go @@ -2,156 +2,82 @@ package cachemanager import ( "context" - "errors" "github.com/fthvgb1/wp-go/cache" "github.com/fthvgb1/wp-go/cache/reload" "github.com/fthvgb1/wp-go/helper" + "github.com/fthvgb1/wp-go/helper/slice" str "github.com/fthvgb1/wp-go/helper/strings" "github.com/fthvgb1/wp-go/safety" "runtime" + "sync" "time" ) -var mapFlush = safety.NewMap[string, func(any)]() -var anyFlush = safety.NewMap[string, func()]() +var mutex sync.Mutex -var varCache = safety.NewMap[string, any]() -var mapCache = safety.NewMap[string, any]() - -func SetVarCache[T any](name string, v *cache.VarCache[T]) error { - vv, ok := varCache.Load(name) - if !ok { - varCache.Store(name, v) - return nil - } - _, ok = vv.(*cache.VarCache[T]) - if ok { - varCache.Store(name, v) - return nil - } - return errors.New(str.Join("cache ", name, " type err")) +type Queue struct { + Name string + Fn func(context.Context) } -func SetMapCache[K comparable, V any](name string, ca *cache.MapCache[K, V]) error { - v, ok := mapCache.Load(name) - if !ok { - mapCache.Store(name, ca) - return nil - } - _, ok = v.(*cache.MapCache[K, V]) - if !ok { - return errors.New(str.Join("cache ", name, " type err")) - } - mapCache.Store(name, ca) - return nil +type Queues []Queue + +func (q *Queues) Get(name string) Queue { + _, v := slice.SearchFirst(*q, func(t Queue) bool { + return name == t.Name + }) + return v } -type flush interface { - Flush(ctx context.Context) +func (q *Queues) Set(name string, fn func(context.Context)) { + i := slice.IndexOfBy(*q, func(t Queue) bool { + return name == t.Name + }) + if i > -1 { + (*q)[i].Fn = fn + return + } + *q = append(*q, Queue{name, fn}) +} + +func (q *Queues) Del(name string) { + i := slice.IndexOfBy(*q, func(t Queue) bool { + return name == t.Name + }) + if i > -1 { + slice.Delete((*[]Queue)(q), i) + } } type clearExpired interface { ClearExpired(ctx context.Context) } -var clears []clearExpired +var clears = safety.NewVar(Queues{}) -var flushes []flush +var flushes = safety.NewVar(Queues{}) func Flush() { ctx := context.WithValue(context.Background(), "execFlushBy", "mangerFlushFn") - for _, f := range flushes { - f.Flush(ctx) + for _, f := range flushes.Load() { + f.Fn(ctx) } } -func FlushMapVal[T any](name string, keys ...T) { - v, ok := mapFlush.Load(name) - if !ok || len(keys) < 1 { - return - } - v(keys) +func Flushes(ctx context.Context, names ...string) { + execute(ctx, flushes, names...) } -func FlushAnyVal(name ...string) { - for _, s := range name { - v, ok := anyFlush.Load(s) - if ok { - v() +func execute(ctx context.Context, q *safety.Var[Queues], names ...string) { + queues := q.Load() + for _, name := range names { + queue := queues.Get(name) + if queue.Fn != nil { + queue.Fn(ctx) } } } -func PushMangerMap[K comparable, V any](name string, m *cache.MapCache[K, V]) { - if name == "" { - return - } - mapCache.Store(name, m) - anyFlush.Store(name, func() { - ctx := context.WithValue(context.Background(), "ctx", "registerFlush") - m.Flush(ctx) - }) - mapFlush.Store(name, func(a any) { - k, ok := a.([]K) - if ok && len(k) > 0 { - ctx := context.WithValue(context.Background(), "ctx", "registerFlush") - m.Del(ctx, k...) - } - }) -} - -func GetBy[T any, K comparable](name string, ct context.Context, key K, timeout time.Duration, params ...any) (r T, err error) { - ct = context.WithValue(ct, "getCache", name) - ca, err := getMap[K, T](name) - if err != nil { - return r, err - } - vv, err := ca.GetCache(ct, key, timeout, params...) - if err != nil { - return r, err - } - r = vv - return -} - -func getMap[K comparable, T any](name string) (*cache.MapCache[K, T], error) { - m, ok := mapCache.Load(name) - if !ok { - return nil, errors.New(str.Join("cache ", name, " doesn't exist")) - } - vk, ok := m.(*cache.MapCache[K, T]) - if !ok { - return nil, errors.New(str.Join("cache ", name, " type error")) - } - return vk, nil -} -func GetBatchBy[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r []T, err error) { - ct = context.WithValue(ct, "getCache", name) - ca, err := getMap[K, T](name) - if err != nil { - return r, err - } - vv, err := ca.GetCacheBatch(ct, key, timeout, params...) - if err != nil { - return r, err - } - r = vv - return -} -func GetBatchByToMap[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r map[K]T, err error) { - ct = context.WithValue(ct, "getCache", name) - ca, err := getMap[K, T](name) - if err != nil { - return r, err - } - vv, err := ca.GetBatchToMap(ct, key, timeout, params...) - if err != nil { - return r, err - } - r = vv - return -} - func parseArgs(args ...any) (string, func() time.Duration) { var name string var fn func() time.Duration @@ -170,42 +96,6 @@ func parseArgs(args ...any) (string, func() time.Duration) { return name, fn } -func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.PaginationData[V]], maxNum int, - dbFn cache.DbFn[K, V], localFn cache.LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, fetchNum int, name string, a ...any) *cache.Pagination[K, V] { - fn := helper.ParseArgs([]func() int(nil), a...) - var ma, fet func() int - if len(fn) > 0 { - ma = fn[0] - if len(fn) > 1 { - fet = fn[1] - } - } - if ma == nil { - ma = reload.BuildFnVal(str.Join("paginationCache-", name, "-maxNum"), maxNum, nil) - } - if fet == nil { - fet = reload.BuildFnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil) - } - p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name) - mapCache.Store(name, p) - return p -} - -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] { - inc := helper.ParseArgs((*cache.IncreaseUpdate[K, V])(nil), args...) - m := cache.NewMapCache[K, V](data, fn, batchFn, inc, buildLockFn[K](args...), args...) - FlushPush(m) - ClearPush(m) - name, f := parseArgs(args...) - if name != "" { - PushMangerMap(name, m) - } - if f != nil && name != "" { - SetExpireTime(any(data).(cache.SetTime), name, 0, f) - } - return m -} - func buildLockFn[K comparable](args ...any) cache.LockFn[K] { lockFn := helper.ParseArgs(cache.LockFn[K](nil), args...) name := helper.ParseArgs("", args...) @@ -228,20 +118,15 @@ func buildLockFn[K comparable](args ...any) cache.LockFn[K] { } else { lo := cache.NewLocks[K](loFn) lockFn = lo.GetLock - FlushPush(lo) + PushOrSetFlush(Queue{ + Name: name, + Fn: lo.Flush, + }) } } return lockFn } -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] { - - c := NewMapCache[K, V](cache.NewMemoryMapCache[K, V](func() time.Duration { - return expireTime - }), batchFn, fn, args...) - return c -} func SetExpireTime(c cache.SetTime, name string, expireTime time.Duration, expireTimeFn func() time.Duration) { if name == "" { @@ -256,95 +141,55 @@ func ChangeExpireTime(t time.Duration, coverConf bool, name ...string) { reload.SetFnVal(s, t, coverConf) } } - -func FlushPush(f ...flush) { - flushes = append(flushes, f...) +func pushOrSet(q *safety.Var[Queues], queues ...Queue) { + mutex.Lock() + defer mutex.Unlock() + qu := q.Load() + for _, queue := range queues { + v := qu.Get(queue.Name) + if v.Fn != nil { + qu.Set(queue.Name, queue.Fn) + } else { + qu = append(qu, queue) + } + } + q.Store(qu) } -func ClearPush(c ...clearExpired) { - clears = append(clears, c...) + +// PushOrSetFlush will execute flush func when call Flush or Flushes +func PushOrSetFlush(queues ...Queue) { + pushOrSet(flushes, queues...) +} + +// PushOrSetClearExpired will execute clearExpired func when call ClearExpired or ClearExpireds +func PushOrSetClearExpired(queues ...Queue) { + pushOrSet(clears, queues...) +} + +func del(q *safety.Var[Queues], names ...string) { + mutex.Lock() + defer mutex.Unlock() + queues := q.Load() + for _, name := range names { + queues.Del(name) + } + q.Store(queues) +} +func DelFlush(names ...string) { + del(flushes, names...) +} + +func DelClearExpired(names ...string) { + del(clears, names...) +} + +func ClearExpireds(ctx context.Context, names ...string) { + execute(ctx, clears, names...) } func ClearExpired() { ctx := context.WithValue(context.Background(), "execClearExpired", "mangerClearExpiredFn") - for _, c := range clears { - c.ClearExpired(ctx) + for _, queue := range clears.Load() { + queue.Fn(ctx) } } - -func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T, error), a ...any) *cache.VarCache[T] { - inc := helper.ParseArgs((*cache.IncreaseUpdateVar[T])(nil), a...) - ref := helper.ParseArgs(cache.RefreshVar[T](nil), a...) - v := cache.NewVarCache(c, fn, inc, ref, a...) - FlushPush(v) - name, _ := parseArgs(a...) - if name != "" { - varCache.Store(name, v) - } - cc, ok := any(c).(clearExpired) - if ok { - ClearPush(cc) - } - return v -} - -func GetVarVal[T any](name string, ctx context.Context, duration time.Duration, a ...any) (r T, err error) { - ctx = context.WithValue(ctx, "getCache", name) - ca, ok := GetVarCache[T](name) - if !ok { - err = errors.New(str.Join("cache ", name, " is not exist")) - return - } - v, err := ca.GetCache(ctx, duration, a...) - if err != nil { - return - } - r = v - return -} - -func NewVarMemoryCache[T any](fn func(context.Context, ...any) (T, error), expired time.Duration, a ...any) *cache.VarCache[T] { - c := cache.NewVarMemoryCache[T](nil) - name, e := parseArgs(a...) - SetExpireTime(c, name, expired, e) - v := NewVarCache[T](c, fn, a...) - return v -} - -func GetVarCache[T any](name string) (*cache.VarCache[T], bool) { - v, ok := varCache.Load(name) - if !ok { - return nil, false - } - vv, ok := v.(*cache.VarCache[T]) - return vv, ok -} - -func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) { - vv, err := getMap[K, V](name) - return vv, err == nil -} - -func GetPaginationCache[K comparable, V any](name string) (*cache.Pagination[K, V], bool) { - v, err := getPagination[K, V](name) - return v, err == nil -} - -func Pagination[V any, K comparable](name string, ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) { - v, err := getPagination[K, V](name) - if err != nil { - return nil, 0, err - } - return v.Pagination(ctx, timeout, k, page, limit, a...) -} - -func getPagination[K comparable, T any](name string) (*cache.Pagination[K, T], error) { - m, ok := mapCache.Load(name) - if !ok { - return nil, errors.New(str.Join("cache ", name, " doesn't exist")) - } - vk, ok := m.(*cache.Pagination[K, T]) - if !ok { - return nil, errors.New(str.Join("cache ", name, " type error")) - } - return vk, nil -} diff --git a/cache/cachemanager/manger_test.go b/cache/cachemanager/manger_test.go index ce1222a..18cfb97 100644 --- a/cache/cachemanager/manger_test.go +++ b/cache/cachemanager/manger_test.go @@ -45,7 +45,7 @@ func TestFlushMapVal(t *testing.T) { } p.Wait() fmt.Println(gets, count) - FlushMapVal("test", 3, 4) + DelMapCacheVal("test", 3, 4) fmt.Println(vv.Get(ctx, 3)) fmt.Println(vv.Get(ctx, 4)) get, err := GetBy[int]("test", ctx, 3, time.Second) @@ -54,7 +54,7 @@ func TestFlushMapVal(t *testing.T) { } fmt.Println(get, count) fmt.Println(vv.Get(ctx, 5)) - FlushAnyVal("test") + Flushes(ctx, "test") fmt.Println(vv.Get(ctx, 5)) fmt.Println(vv.Get(ctx, 6)) //fmt.Println(GetVarCache("test")) diff --git a/cache/cachemanager/mapcache.go b/cache/cachemanager/mapcache.go new file mode 100644 index 0000000..466a38c --- /dev/null +++ b/cache/cachemanager/mapcache.go @@ -0,0 +1,147 @@ +package cachemanager + +import ( + "context" + "errors" + "github.com/fthvgb1/wp-go/cache" + "github.com/fthvgb1/wp-go/helper" + str "github.com/fthvgb1/wp-go/helper/strings" + "github.com/fthvgb1/wp-go/safety" + "time" +) + +var mapDelFuncs = safety.NewMap[string, func(any)]() + +var mapCache = safety.NewMap[string, any]() + +func SetMapCache[K comparable, V any](name string, ca *cache.MapCache[K, V]) error { + v, ok := mapCache.Load(name) + if !ok { + mapCache.Store(name, ca) + return nil + } + _, ok = v.(*cache.MapCache[K, V]) + if !ok { + return errors.New(str.Join("cache ", name, " type err")) + } + mapCache.Store(name, ca) + return nil +} + +// PushMangerMap will del mapCache val with name When call DelMapCacheVal +func PushMangerMap[K comparable, V any](name string, m *cache.MapCache[K, V]) { + if name == "" { + return + } + mapCache.Store(name, m) + mapDelFuncs.Store(name, func(a any) { + k, ok := a.([]K) + if ok && len(k) > 0 { + mm, ok := mapCache.Load(name) + if !ok { + return + } + c, ok := mm.(*cache.MapCache[K, V]) + if !ok { + return + } + ctx := context.WithValue(context.Background(), "ctx", "registerFlush") + c.Del(ctx, k...) + } + }) +} + +func GetBy[T any, K comparable](name string, ct context.Context, key K, timeout time.Duration, params ...any) (r T, err error) { + ct = context.WithValue(ct, "getCache", name) + ca, err := getMap[K, T](name) + if err != nil { + return r, err + } + vv, err := ca.GetCache(ct, key, timeout, params...) + if err != nil { + return r, err + } + r = vv + return +} + +func getMap[K comparable, T any](name string) (*cache.MapCache[K, T], error) { + m, ok := mapCache.Load(name) + if !ok { + return nil, errors.New(str.Join("cache ", name, " doesn't exist")) + } + vk, ok := m.(*cache.MapCache[K, T]) + if !ok { + return nil, errors.New(str.Join("cache ", name, " type error")) + } + return vk, nil +} +func GetBatchBy[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r []T, err error) { + ct = context.WithValue(ct, "getCache", name) + ca, err := getMap[K, T](name) + if err != nil { + return r, err + } + vv, err := ca.GetCacheBatch(ct, key, timeout, params...) + if err != nil { + return r, err + } + r = vv + return +} +func GetBatchByToMap[T any, K comparable](name string, ct context.Context, key []K, timeout time.Duration, params ...any) (r map[K]T, err error) { + ct = context.WithValue(ct, "getCache", name) + ca, err := getMap[K, T](name) + if err != nil { + return r, err + } + vv, err := ca.GetBatchToMap(ct, key, timeout, params...) + if err != nil { + return r, err + } + r = vv + return +} + +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] { + inc := helper.ParseArgs((*cache.IncreaseUpdate[K, V])(nil), args...) + m := cache.NewMapCache[K, V](data, fn, batchFn, inc, buildLockFn[K](args...), args...) + name, f := parseArgs(args...) + if name != "" { + PushMangerMap(name, m) + } + PushOrSetFlush(Queue{ + Name: name, + Fn: m.Flush, + }) + PushOrSetClearExpired(Queue{ + Name: name, + Fn: m.ClearExpired, + }) + if f != nil && name != "" { + SetExpireTime(any(data).(cache.SetTime), name, 0, f) + } + return m +} + +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] { + + c := NewMapCache[K, V](cache.NewMemoryMapCache[K, V](func() time.Duration { + return expireTime + }), batchFn, fn, args...) + return c +} + +func GetMapCache[K comparable, V any](name string) (*cache.MapCache[K, V], bool) { + vv, err := getMap[K, V](name) + return vv, err == nil +} + +func DelMapCacheVal[T any](name string, keys ...T) { + v, ok := mapDelFuncs.Load(name) + if !ok || len(keys) < 1 { + return + } + v(keys) +} diff --git a/cache/cachemanager/pagination.go b/cache/cachemanager/pagination.go new file mode 100644 index 0000000..e1431a8 --- /dev/null +++ b/cache/cachemanager/pagination.go @@ -0,0 +1,57 @@ +package cachemanager + +import ( + "context" + "errors" + "github.com/fthvgb1/wp-go/cache" + "github.com/fthvgb1/wp-go/cache/reload" + "github.com/fthvgb1/wp-go/helper" + str "github.com/fthvgb1/wp-go/helper/strings" + "time" +) + +func NewPaginationCache[K comparable, V any](m *cache.MapCache[string, helper.PaginationData[V]], maxNum int, + dbFn cache.DbFn[K, V], localFn cache.LocalFn[K, V], dbKeyFn, localKeyFn func(K, ...any) string, fetchNum int, name string, a ...any) *cache.Pagination[K, V] { + fn := helper.ParseArgs([]func() int(nil), a...) + var ma, fet func() int + if len(fn) > 0 { + ma = fn[0] + if len(fn) > 1 { + fet = fn[1] + } + } + if ma == nil { + ma = reload.BuildFnVal(str.Join("paginationCache-", name, "-maxNum"), maxNum, nil) + } + if fet == nil { + fet = reload.BuildFnVal(str.Join("paginationCache-", name, "-fetchNum"), fetchNum, nil) + } + p := cache.NewPagination(m, ma, dbFn, localFn, dbKeyFn, localKeyFn, fet, name) + mapCache.Store(name, p) + return p +} + +func GetPaginationCache[K comparable, V any](name string) (*cache.Pagination[K, V], bool) { + v, err := getPagination[K, V](name) + return v, err == nil +} + +func Pagination[V any, K comparable](name string, ctx context.Context, timeout time.Duration, k K, page, limit int, a ...any) ([]V, int, error) { + v, err := getPagination[K, V](name) + if err != nil { + return nil, 0, err + } + return v.Pagination(ctx, timeout, k, page, limit, a...) +} + +func getPagination[K comparable, T any](name string) (*cache.Pagination[K, T], error) { + m, ok := mapCache.Load(name) + if !ok { + return nil, errors.New(str.Join("cache ", name, " doesn't exist")) + } + vk, ok := m.(*cache.Pagination[K, T]) + if !ok { + return nil, errors.New(str.Join("cache ", name, " type error")) + } + return vk, nil +} diff --git a/cache/cachemanager/varcache.go b/cache/cachemanager/varcache.go new file mode 100644 index 0000000..f5f91d4 --- /dev/null +++ b/cache/cachemanager/varcache.go @@ -0,0 +1,82 @@ +package cachemanager + +import ( + "context" + "errors" + "github.com/fthvgb1/wp-go/cache" + "github.com/fthvgb1/wp-go/helper" + str "github.com/fthvgb1/wp-go/helper/strings" + "github.com/fthvgb1/wp-go/safety" + "time" +) + +var varCache = safety.NewMap[string, any]() + +func SetVarCache[T any](name string, v *cache.VarCache[T]) error { + vv, ok := varCache.Load(name) + if !ok { + varCache.Store(name, v) + return nil + } + _, ok = vv.(*cache.VarCache[T]) + if ok { + varCache.Store(name, v) + return nil + } + return errors.New(str.Join("cache ", name, " type err")) +} + +func NewVarCache[T any](c cache.AnyCache[T], fn func(context.Context, ...any) (T, error), a ...any) *cache.VarCache[T] { + inc := helper.ParseArgs((*cache.IncreaseUpdateVar[T])(nil), a...) + ref := helper.ParseArgs(cache.RefreshVar[T](nil), a...) + v := cache.NewVarCache(c, fn, inc, ref, a...) + + name, _ := parseArgs(a...) + if name != "" { + varCache.Store(name, v) + } + PushOrSetFlush(Queue{ + Name: name, + Fn: v.Flush, + }) + cc, ok := any(c).(clearExpired) + if ok { + PushOrSetClearExpired(Queue{ + Name: name, + Fn: cc.ClearExpired, + }) + } + return v +} + +func GetVarVal[T any](name string, ctx context.Context, duration time.Duration, a ...any) (r T, err error) { + ctx = context.WithValue(ctx, "getCache", name) + ca, ok := GetVarCache[T](name) + if !ok { + err = errors.New(str.Join("cache ", name, " is not exist")) + return + } + v, err := ca.GetCache(ctx, duration, a...) + if err != nil { + return + } + r = v + return +} + +func NewVarMemoryCache[T any](fn func(context.Context, ...any) (T, error), expired time.Duration, a ...any) *cache.VarCache[T] { + c := cache.NewVarMemoryCache[T](nil) + name, e := parseArgs(a...) + SetExpireTime(c, name, expired, e) + v := NewVarCache[T](c, fn, a...) + return v +} + +func GetVarCache[T any](name string) (*cache.VarCache[T], bool) { + v, ok := varCache.Load(name) + if !ok { + return nil, false + } + vv, ok := v.(*cache.VarCache[T]) + return vv, ok +} diff --git a/helper/slice/slice.go b/helper/slice/slice.go index 4d599ee..52db2ca 100644 --- a/helper/slice/slice.go +++ b/helper/slice/slice.go @@ -298,6 +298,15 @@ func IndexOf[T comparable](a []T, v T) int { } return -1 } +func IndexOfBy[T any](a []T, fn func(T) bool) int { + for i, t := range a { + ok := fn(t) + if ok { + return i + } + } + return -1 +} func ForEach[T any](a []T, fn func(i int, v T)) { for i, t := range a {