|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/zijiren233/gencontainer/refreshcache"
|
|
|
|
"golang.org/x/exp/maps"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MapRefreshFunc[T any, A any] func(ctx context.Context, id string, args ...A) (T, error)
|
|
|
|
|
|
|
|
type MapCache[T any, A any] struct {
|
|
|
|
lock sync.RWMutex
|
|
|
|
cache map[string]*refreshcache.RefreshCache[T, A]
|
|
|
|
refreshFunc MapRefreshFunc[T, A]
|
|
|
|
maxAge time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMapCache[T any, A any](refreshFunc MapRefreshFunc[T, A], maxAge time.Duration) *MapCache[T, A] {
|
|
|
|
return &MapCache[T, A]{
|
|
|
|
cache: make(map[string]*refreshcache.RefreshCache[T, A]),
|
|
|
|
refreshFunc: refreshFunc,
|
|
|
|
maxAge: maxAge,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) Clear() {
|
|
|
|
b.lock.Lock()
|
|
|
|
defer b.lock.Unlock()
|
|
|
|
b.clear()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) clear() {
|
|
|
|
maps.Clear(b.cache)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) LoadOrStore(ctx context.Context, id string) (T, error) {
|
|
|
|
b.lock.RLock()
|
|
|
|
c, loaded := b.cache[id]
|
|
|
|
if loaded {
|
|
|
|
b.lock.RUnlock()
|
|
|
|
return c.Get(ctx)
|
|
|
|
}
|
|
|
|
b.lock.RUnlock()
|
|
|
|
b.lock.Lock()
|
|
|
|
c, loaded = b.cache[id]
|
|
|
|
if loaded {
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Get(ctx)
|
|
|
|
}
|
|
|
|
c = refreshcache.NewRefreshCache[T, A](func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return b.refreshFunc(ctx, id, args...)
|
|
|
|
}, b.maxAge)
|
|
|
|
b.cache[id] = c
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Get(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) StoreOrRefresh(ctx context.Context, id string) (T, error) {
|
|
|
|
b.lock.RLock()
|
|
|
|
c, ok := b.cache[id]
|
|
|
|
if ok {
|
|
|
|
b.lock.RUnlock()
|
|
|
|
return c.Refresh(ctx)
|
|
|
|
}
|
|
|
|
b.lock.RUnlock()
|
|
|
|
b.lock.Lock()
|
|
|
|
c, ok = b.cache[id]
|
|
|
|
if ok {
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Refresh(ctx)
|
|
|
|
}
|
|
|
|
c = refreshcache.NewRefreshCache[T, A](func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return b.refreshFunc(ctx, id, args...)
|
|
|
|
}, b.maxAge)
|
|
|
|
b.cache[id] = c
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Refresh(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) LoadOrStoreWithDynamicFunc(ctx context.Context, id string, refreshFunc MapRefreshFunc[T, A]) (T, error) {
|
|
|
|
b.lock.RLock()
|
|
|
|
c, loaded := b.cache[id]
|
|
|
|
if loaded {
|
|
|
|
b.lock.RUnlock()
|
|
|
|
return c.Data().Get(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
b.lock.RUnlock()
|
|
|
|
b.lock.Lock()
|
|
|
|
c, loaded = b.cache[id]
|
|
|
|
if loaded {
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Data().Get(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
c = refreshcache.NewRefreshCache[T, A](func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return b.refreshFunc(ctx, id, args...)
|
|
|
|
}, b.maxAge)
|
|
|
|
b.cache[id] = c
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Data().Get(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *MapCache[T, A]) StoreOrRefreshWithDynamicFunc(ctx context.Context, id string, refreshFunc MapRefreshFunc[T, A]) (T, error) {
|
|
|
|
b.lock.RLock()
|
|
|
|
c, ok := b.cache[id]
|
|
|
|
if ok {
|
|
|
|
b.lock.RUnlock()
|
|
|
|
return c.Data().Refresh(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
b.lock.RUnlock()
|
|
|
|
b.lock.Lock()
|
|
|
|
c, ok = b.cache[id]
|
|
|
|
if ok {
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Data().Refresh(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
c = refreshcache.NewRefreshCache[T, A](func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return b.refreshFunc(ctx, id, args...)
|
|
|
|
}, b.maxAge)
|
|
|
|
b.cache[id] = c
|
|
|
|
b.lock.Unlock()
|
|
|
|
return c.Data().Refresh(ctx, func(ctx context.Context, args ...A) (T, error) {
|
|
|
|
return refreshFunc(ctx, id, args...)
|
|
|
|
})
|
|
|
|
}
|