114 lines
2.4 KiB
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
|
|
}
|
|
}
|