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-10-26 13:38:31 +00:00
|
|
|
handle Cache[K, V]
|
2023-02-02 11:16:18 +00:00
|
|
|
mux sync.Mutex
|
2023-10-26 13:38:31 +00:00
|
|
|
cacheFunc MapSingleFn[K, V]
|
|
|
|
batchCacheFn MapBatchFn[K, V]
|
2022-10-08 06:01:05 +00:00
|
|
|
expireTime time.Duration
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 13:38:31 +00:00
|
|
|
type MapSingleFn[K, V any] func(context.Context, K, ...any) (V, error)
|
|
|
|
type MapBatchFn[K comparable, V any] func(context.Context, []K, ...any) (map[K]V, error)
|
|
|
|
|
|
|
|
func NewMapCache[K comparable, V any](data Cache[K, V], cacheFunc MapSingleFn[K, V], batchCacheFn MapBatchFn[K, V], expireTime time.Duration) *MapCache[K, V] {
|
|
|
|
r := &MapCache[K, V]{
|
|
|
|
handle: data,
|
|
|
|
mux: sync.Mutex{},
|
|
|
|
cacheFunc: cacheFunc,
|
|
|
|
batchCacheFn: batchCacheFn,
|
|
|
|
expireTime: expireTime,
|
|
|
|
}
|
|
|
|
if cacheFunc == nil && batchCacheFn != nil {
|
|
|
|
r.setDefaultCacheFn(batchCacheFn)
|
|
|
|
} else if batchCacheFn == nil && cacheFunc != nil {
|
|
|
|
r.SetDefaultBatchFunc(cacheFunc)
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapCache[K, V]) SetDefaultBatchFunc(fn MapSingleFn[K, V]) {
|
|
|
|
m.batchCacheFn = func(ctx context.Context, ids []K, a ...any) (map[K]V, error) {
|
|
|
|
var err error
|
|
|
|
rr := make(map[K]V)
|
|
|
|
for _, id := range ids {
|
|
|
|
v, er := fn(ctx, id)
|
|
|
|
if er != nil {
|
|
|
|
err = errors.Join(er)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
rr[id] = v
|
|
|
|
}
|
|
|
|
return rr, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapCache[K, V]) SetCacheFunc(fn MapSingleFn[K, V]) {
|
2022-10-08 06:01:05 +00:00
|
|
|
m.cacheFunc = fn
|
2022-09-27 13:52:15 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
func (m *MapCache[K, V]) GetHandle() Cache[K, V] {
|
|
|
|
return m.handle
|
|
|
|
}
|
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 {
|
2023-10-26 13:38:31 +00:00
|
|
|
return m.handle.Ttl(ctx, k, m.expireTime)
|
2023-02-02 14:56:09 +00:00
|
|
|
}
|
|
|
|
|
2023-02-02 11:16:18 +00:00
|
|
|
func (m *MapCache[K, V]) GetLastSetTime(ctx context.Context, k K) (t time.Time) {
|
2023-10-26 13:38:31 +00:00
|
|
|
tt := m.handle.Ttl(ctx, k, m.expireTime)
|
2023-02-02 11:16:18 +00:00
|
|
|
if tt <= 0 {
|
|
|
|
return
|
2022-10-07 14:27:34 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
return time.Now().Add(m.handle.Ttl(ctx, k, m.expireTime)).Add(-m.expireTime)
|
2022-10-07 14:27:34 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 13:38:31 +00:00
|
|
|
func (m *MapCache[K, V]) SetCacheBatchFn(fn MapBatchFn[K, V]) {
|
2022-10-08 06:01:05 +00:00
|
|
|
m.batchCacheFn = fn
|
|
|
|
if m.cacheFunc == nil {
|
2023-10-26 13:38:31 +00:00
|
|
|
m.setDefaultCacheFn(fn)
|
2022-10-08 06:01:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-26 13:38:31 +00:00
|
|
|
func (m *MapCache[K, V]) setDefaultCacheFn(fn MapBatchFn[K, V]) {
|
|
|
|
m.cacheFunc = func(ctx context.Context, k K, a ...any) (V, error) {
|
2022-11-07 08:04:13 +00:00
|
|
|
var err error
|
|
|
|
var r map[K]V
|
2023-10-26 13:38:31 +00:00
|
|
|
r, err = fn(ctx, []K{k}, a...)
|
2022-11-07 08:04:13 +00:00
|
|
|
|
2022-10-08 06:01:05 +00:00
|
|
|
if err != nil {
|
|
|
|
var rr V
|
|
|
|
return rr, err
|
|
|
|
}
|
2023-02-28 07:17:16 +00:00
|
|
|
return r[k], err
|
2022-10-08 06:01:05 +00:00
|
|
|
}
|
2022-09-27 13:52:15 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 13:38:31 +00:00
|
|
|
func NewMapCacheByFn[K comparable, V any](cacheType Cache[K, V], fn MapSingleFn[K, V], expireTime time.Duration) *MapCache[K, V] {
|
|
|
|
r := &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-10-26 13:38:31 +00:00
|
|
|
handle: cacheType,
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
r.SetDefaultBatchFunc(fn)
|
|
|
|
return r
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
func NewMapCacheByBatchFn[K comparable, V any](cacheType Cache[K, V], fn MapBatchFn[K, V], 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-10-26 13:38:31 +00:00
|
|
|
handle: cacheType,
|
2022-09-20 08:11:20 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
r.setDefaultCacheFn(fn)
|
2022-10-08 06:01:05 +00:00
|
|
|
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()
|
2023-10-26 13:38:31 +00:00
|
|
|
m.handle.Flush(ctx)
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
|
2023-02-02 11:16:18 +00:00
|
|
|
func (m *MapCache[K, V]) Get(ctx context.Context, k K) (V, bool) {
|
2023-10-26 13:38:31 +00:00
|
|
|
return m.handle.Get(ctx, k)
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
|
2023-02-02 11:16:18 +00:00
|
|
|
func (m *MapCache[K, V]) Set(ctx context.Context, k K, v V) {
|
2023-10-26 13:38:31 +00:00
|
|
|
m.handle.Set(ctx, k, v, m.expireTime)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapCache[K, V]) Delete(ctx context.Context, k K) {
|
|
|
|
m.handle.Delete(ctx, k)
|
|
|
|
}
|
|
|
|
func (m *MapCache[K, V]) Ver(ctx context.Context, k K) int {
|
|
|
|
return m.handle.Ver(ctx, k)
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
|
2023-10-26 13:38:31 +00:00
|
|
|
data, ok := m.handle.Get(c, key)
|
2022-09-20 08:11:20 +00:00
|
|
|
var err error
|
2023-10-26 13:38:31 +00:00
|
|
|
if !ok || m.handle.Ttl(c, key, m.expireTime) <= 0 {
|
|
|
|
ver := m.handle.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()
|
2023-10-26 13:38:31 +00:00
|
|
|
if m.handle.Ver(c, key) > ver {
|
|
|
|
data, _ = m.handle.Get(c, key)
|
2022-10-14 09:15:43 +00:00
|
|
|
return
|
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
data, err = m.cacheFunc(c, key, 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)
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
if timeout > 0 {
|
|
|
|
ctx, cancel := context.WithTimeout(c, timeout)
|
|
|
|
defer cancel()
|
2022-12-07 05:26:52 +00:00
|
|
|
done := make(chan struct{}, 1)
|
2022-09-26 13:25:41 +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
|
|
|
return data, err
|
2022-09-26 13:25:41 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2023-10-26 13:38:31 +00:00
|
|
|
if _, ok := m.handle.Get(c, k); !ok {
|
2023-02-02 11:16:18 +00:00
|
|
|
return k, true
|
2022-09-27 13:52:15 +00:00
|
|
|
}
|
2023-10-26 13:38:31 +00:00
|
|
|
ver += m.handle.Ver(c, k)
|
2023-02-02 11:16:18 +00:00
|
|
|
return
|
|
|
|
})
|
|
|
|
|
2022-09-26 13:25:41 +00:00
|
|
|
var err error
|
2022-09-27 13:52:15 +00:00
|
|
|
if len(needFlush) > 0 {
|
2022-09-26 13:25:41 +00:00
|
|
|
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 {
|
2023-10-26 13:38:31 +00:00
|
|
|
return r + m.handle.Ver(c, t)
|
2023-02-02 11:16:18 +00:00
|
|
|
}, 0)
|
|
|
|
|
|
|
|
if vers > ver {
|
2022-09-26 13:25:41 +00:00
|
|
|
return
|
|
|
|
}
|
2023-02-02 11:16:18 +00:00
|
|
|
|
2023-10-26 13:38:31 +00:00
|
|
|
r, er := m.batchCacheFn(c, key, params...)
|
2022-09-26 13:25:41 +00:00
|
|
|
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-26 13:25:41 +00:00
|
|
|
}
|
2022-09-20 08:11:20 +00:00
|
|
|
}
|
|
|
|
if timeout > 0 {
|
2022-09-26 13:25:41 +00:00
|
|
|
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) {
|
2023-10-26 13:38:31 +00:00
|
|
|
return m.handle.Get(c, k)
|
2023-02-02 11:16:18 +00:00
|
|
|
})
|
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()
|
2023-10-26 13:38:31 +00:00
|
|
|
m.handle.ClearExpired(ctx, m.expireTime)
|
2022-09-28 12:02:43 +00:00
|
|
|
}
|