wp-go/cache/map.go

200 lines
4.2 KiB
Go
Raw Permalink Normal View History

2022-09-20 08:11:20 +00:00
package cache
import (
"context"
"errors"
"fmt"
2023-02-02 11:16:18 +00:00
"github.com/fthvgb1/wp-go/helper/slice"
2022-09-20 08:11:20 +00:00
"sync"
"time"
)
type MapCache[K comparable, V any] struct {
2023-02-02 11:16:18 +00:00
data Cache[K, V]
mux sync.Mutex
2022-10-08 06:01:05 +00:00
cacheFunc func(...any) (V, error)
batchCacheFn func(...any) (map[K]V, error)
expireTime time.Duration
}
2022-09-27 13:52:15 +00:00
func (m *MapCache[K, V]) SetCacheFunc(fn func(...any) (V, error)) {
2022-10-08 06:01:05 +00:00
m.cacheFunc = fn
2022-09-27 13:52:15 +00:00
}
2023-02-02 14:56:09 +00:00
func (m *MapCache[K, V]) Ttl(ctx context.Context, k K) time.Duration {
return m.data.Ttl(ctx, k, m.expireTime)
}
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
tt := m.data.Ttl(ctx, k, m.expireTime)
if tt <= 0 {
return
2022-10-07 14:27:34 +00:00
}
2023-02-02 14:56:09 +00:00
return time.Now().Add(m.data.Ttl(ctx, k, m.expireTime)).Add(-m.expireTime)
2022-10-07 14:27:34 +00:00
}
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) SetCacheBatchFn(fn func(...any) (map[K]V, error)) {
2022-10-08 06:01:05 +00:00
m.batchCacheFn = fn
if m.cacheFunc == nil {
m.setCacheFn(fn)
}
}
func (m *MapCache[K, V]) setCacheFn(fn func(...any) (map[K]V, error)) {
m.cacheFunc = func(a ...any) (V, error) {
var err error
var r map[K]V
var id K
ctx, ok := a[0].(context.Context)
if ok {
id = a[1].(K)
r, err = fn(ctx, []K{id})
} else {
id = a[0].(K)
r, err = fn([]K{id})
}
2022-10-08 06:01:05 +00:00
if err != nil {
var rr V
return rr, err
}
return r[id], err
}
2022-09-27 13:52:15 +00:00
}
2023-02-02 14:56:09 +00:00
func NewMapCacheByFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
2022-09-20 08:11:20 +00:00
return &MapCache[K, V]{
2023-02-02 14:56:09 +00:00
mux: sync.Mutex{},
2022-10-08 06:01:05 +00:00
cacheFunc: fn,
expireTime: expireTime,
2023-02-02 14:56:09 +00:00
data: cacheType,
}
}
2023-02-02 14:56:09 +00:00
func NewMapCacheByBatchFn[K comparable, V any](cacheType Cache[K, V], fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
2022-10-08 06:01:05 +00:00
r := &MapCache[K, V]{
2023-02-02 14:56:09 +00:00
mux: sync.Mutex{},
2022-10-08 06:01:05 +00:00
batchCacheFn: fn,
expireTime: expireTime,
2023-02-02 14:56:09 +00:00
data: cacheType,
2022-09-20 08:11:20 +00:00
}
2022-10-08 06:01:05 +00:00
r.setCacheFn(fn)
return r
2022-09-20 08:11:20 +00:00
}
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) Flush(ctx context.Context) {
m.mux.Lock()
defer m.mux.Unlock()
m.data.Flush(ctx)
}
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) Get(ctx context.Context, k K) (V, bool) {
return m.data.Get(ctx, k)
}
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) Set(ctx context.Context, k K, v V) {
m.data.Set(ctx, k, v, m.expireTime)
}
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
2023-02-02 11:16:18 +00:00
data, ok := m.data.Get(c, key)
2022-09-20 08:11:20 +00:00
var err error
2023-02-02 11:16:18 +00:00
if !ok || m.data.Ttl(c, key, m.expireTime) <= 0 {
ver := m.data.Ver(c, key)
2022-09-20 08:11:20 +00:00
call := func() {
2023-02-02 11:16:18 +00:00
m.mux.Lock()
defer m.mux.Unlock()
if m.data.Ver(c, key) > ver {
data, _ = m.data.Get(c, key)
2022-10-14 09:15:43 +00:00
return
}
2023-02-02 11:16:18 +00:00
data, err = m.cacheFunc(params...)
2022-09-20 08:11:20 +00:00
if err != nil {
return
}
2023-02-02 11:16:18 +00:00
m.Set(c, key, data)
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
2022-12-07 05:26:52 +00:00
done := make(chan struct{}, 1)
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()
}
}
2023-02-02 11:16:18 +00:00
return data, err
}
2022-09-27 13:52:15 +00:00
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key []K, timeout time.Duration, params ...any) ([]V, error) {
var res []V
2023-02-02 11:16:18 +00:00
ver := 0
needFlush := slice.FilterAndMap(key, func(k K) (r K, ok bool) {
if _, ok := m.data.Get(c, k); !ok {
return k, true
2022-09-27 13:52:15 +00:00
}
2023-02-02 11:16:18 +00:00
ver += m.data.Ver(c, k)
return
})
var err error
2022-09-27 13:52:15 +00:00
if len(needFlush) > 0 {
call := func() {
2023-02-02 11:16:18 +00:00
m.mux.Lock()
defer m.mux.Unlock()
vers := slice.Reduce(needFlush, func(t K, r int) int {
r += m.data.Ver(c, t)
return r
}, 0)
if vers > ver {
return
}
2023-02-02 11:16:18 +00:00
2022-10-08 06:01:05 +00:00
r, er := m.batchCacheFn(params...)
if err != nil {
err = er
return
}
for k, v := range r {
2023-02-02 11:16:18 +00:00
m.Set(c, k, v)
}
2022-09-20 08:11:20 +00:00
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout)
2022-09-20 08:11:20 +00:00
defer cancel()
2022-12-07 05:26:52 +00:00
done := make(chan struct{}, 1)
2022-09-20 08:11:20 +00:00
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()
}
}
2023-02-02 11:16:18 +00:00
res = slice.FilterAndMap(key, func(k K) (V, bool) {
return m.data.Get(c, k)
})
2022-09-27 13:52:15 +00:00
return res, err
2022-09-20 08:11:20 +00:00
}
2022-09-28 12:02:43 +00:00
2023-02-02 11:16:18 +00:00
func (m *MapCache[K, V]) ClearExpired(ctx context.Context) {
m.mux.Lock()
defer m.mux.Unlock()
m.data.ClearExpired(ctx, m.expireTime)
2022-09-28 12:02:43 +00:00
}