aeus-admin/pkg/dbcache/cache.go

114 lines
2.4 KiB
Go

package dbcache
import (
"context"
"time"
"git.nobla.cn/golang/aeus/pkg/cache"
"git.nobla.cn/golang/aeus/pkg/errors"
"golang.org/x/sync/singleflight"
"gorm.io/gorm"
)
var singleInstance singleflight.Group
type (
CacheOptions struct {
db *gorm.DB
cache cache.Cache
dependency CacheDependency
cacheDuration time.Duration
}
CacheOption func(o *CacheOptions)
cacheEntry[T any] struct {
Value T
CompareValue string
CreatedAt int64
}
)
func WithDB(db *gorm.DB) CacheOption {
return func(o *CacheOptions) {
o.db = db
}
}
func WithCache(c cache.Cache) CacheOption {
return func(o *CacheOptions) {
o.cache = c
}
}
func WithCacheDuration(d time.Duration) CacheOption {
return func(o *CacheOptions) {
o.cacheDuration = d
}
}
func WithDependency(d CacheDependency) CacheOption {
return func(o *CacheOptions) {
o.dependency = d
}
}
// TryCache 尝试从缓存中获取数据
func TryCache[T any](ctx context.Context, key string, f func(tx *gorm.DB) (T, error), cbs ...CacheOption) (result T, err error) {
var (
none T
value any
val any
hasDependValue bool
dependValue string
)
opts := &CacheOptions{
cache: cache.Default(),
cacheDuration: time.Minute * 10,
}
for _, cb := range cbs {
cb(opts)
}
if opts.db == nil {
return none, errors.Format(errors.Unavailable, "db instance unavailable")
}
//从缓存加载数据
if value, err = opts.cache.Get(ctx, key); err == nil {
if entry, ok := value.(*cacheEntry[T]); ok {
if time.Now().Unix()-entry.CreatedAt <= 1 {
return entry.Value, nil
}
if opts.dependency == nil {
return entry.Value, nil
}
if dependValue, err = opts.dependency.GetValue(ctx, opts.db); err == nil {
hasDependValue = true
if entry.CompareValue == dependValue {
return entry.Value, nil
} else {
opts.cache.Delete(ctx, key)
}
}
}
}
//从数据库加载数据
tx := opts.db.WithContext(ctx)
if val, err, _ = singleInstance.Do(key, func() (any, error) {
if result, err = f(tx); err == nil {
if !hasDependValue && opts.dependency != nil {
dependValue, err = opts.dependency.GetValue(ctx, tx)
}
opts.cache.Put(ctx, key, &cacheEntry[T]{
CompareValue: dependValue,
Value: result,
CreatedAt: time.Now().Unix(),
}, opts.cacheDuration)
}
return result, err
}); err != nil {
return none, err
} else {
return val.(T), err
}
}