182 lines
5.2 KiB
Go
182 lines
5.2 KiB
Go
package manager
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// 公共的事件管理器
|
|
//
|
|
// 作用
|
|
// 1. 对外提供统一的缓存操作接口,比如根据ID获取、更新、删除等
|
|
// 2. 缓存读取不到的时候会自动从DB加载数据,简化业务代码的实现
|
|
// 3. 处理缓存穿透、缓存雪崩等问题,提升系统的稳定性和性能
|
|
|
|
type EventManager[T any] struct {
|
|
tx *gorm.DB
|
|
options *managerOptions
|
|
ttl time.Duration
|
|
manager *CacheManager[T]
|
|
idGetter func(*T) int64
|
|
}
|
|
|
|
func NewEventManager[T any](tx *gorm.DB, ttl time.Duration, manager *CacheManager[T], idGetter func(*T) int64, ops ...OptionFunc) *EventManager[T] {
|
|
options := defaultManagerOptions()
|
|
for _, op := range ops {
|
|
op(options)
|
|
}
|
|
return &EventManager[T]{
|
|
tx: tx,
|
|
options: options,
|
|
ttl: ttl,
|
|
manager: manager,
|
|
idGetter: idGetter,
|
|
}
|
|
}
|
|
|
|
func (l *EventManager[T]) DeleteById(ctx context.Context, id int64) error {
|
|
err := l.manager.Del(ctx, fmt.Sprintf("%d", id))
|
|
if err != nil {
|
|
l.options.logger.Errorf(ctx, "EventManager DeleteById error: %v", err)
|
|
return err
|
|
}
|
|
l.options.logger.Infof(ctx, "EventManager DeleteById id:%d success", id)
|
|
return nil
|
|
}
|
|
|
|
func (l *EventManager[T]) DeleteByKey(ctx context.Context, key string) error {
|
|
err := l.manager.Del(ctx, key)
|
|
if err != nil {
|
|
l.options.logger.Errorf(ctx, "EventManager DeleteByKey error: %v", err)
|
|
return err
|
|
}
|
|
l.options.logger.Infof(ctx, "EventManager DeleteByKey key:%s success", key)
|
|
return nil
|
|
}
|
|
|
|
func (e *EventManager[T]) UpdateById(ctx context.Context, id int64) error {
|
|
_, err := e.UpdateByIdWithValue(ctx, id)
|
|
return err
|
|
}
|
|
|
|
func (e *EventManager[T]) UpdateByIdWithValue(ctx context.Context, id int64) (*T, error) {
|
|
// 查询DB & 更新缓存
|
|
data := new(T)
|
|
err := e.tx.WithContext(ctx).Where("id = ?", id).First(data).Error
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager UpdateByIdWithValue id:%d db query error: %v", id, err)
|
|
return nil, err
|
|
}
|
|
err = e.manager.Set(ctx, fmt.Sprintf("%d", id), data, e.ttl, []int64{id})
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager cache set id:%d error: %v", id, err)
|
|
return nil, err
|
|
}
|
|
e.options.logger.Infof(ctx, "EventManager cache update id:%d", id)
|
|
return data, nil
|
|
}
|
|
|
|
func (e *EventManager[T]) GetById(ctx context.Context, id int64) (*T, error) {
|
|
key := fmt.Sprintf("%d", id)
|
|
data, err := e.manager.Get(ctx, key)
|
|
if err != nil && !errors.Is(err, ErrCacheNil) {
|
|
e.options.logger.Errorf(ctx, "EventManager Get id:%d error: %v", id, err)
|
|
return nil, err
|
|
}
|
|
if data != nil {
|
|
e.options.logger.Infof(ctx, "EventManager get by cache id:%d", id)
|
|
return data, nil
|
|
}
|
|
|
|
// TODO: 缓存雪崩问题,这里需要处理一下,比如设置一个兜底数据之类的
|
|
|
|
d, err := e.UpdateByIdWithValue(ctx, id)
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager UpdateByIdWithValue id:%d err:%v", id, err)
|
|
return nil, err
|
|
}
|
|
return d, nil
|
|
}
|
|
func (e *EventManager[T]) GetByIds(ctx context.Context, ids []int64) ([]*T, error) {
|
|
resp := make([]*T, 0, len(ids))
|
|
|
|
keys := make([]string, 0, len(ids))
|
|
keyMap := make(map[string]struct{}, len(ids))
|
|
for _, id := range ids {
|
|
key := fmt.Sprintf("%d", id)
|
|
keys = append(keys, key)
|
|
keyMap[key] = struct{}{}
|
|
}
|
|
|
|
// global.Logger.Infof(ctx, "get by cache ids:%v keys:%+v", ids, keys)
|
|
|
|
respMap, err := e.manager.BatchGet(ctx, keys)
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager cache batch get ids:%v error: %v", ids, err)
|
|
return nil, err
|
|
}
|
|
// e.options.logger.Infof(ctx, "get by cache ids:%v,respMap:%+v", ids, respMap)
|
|
dbs := make([]string, 0)
|
|
for key := range keyMap {
|
|
if val, ok := respMap[key]; ok {
|
|
resp = append(resp, val)
|
|
} else {
|
|
dbs = append(dbs, key)
|
|
}
|
|
}
|
|
|
|
if len(dbs) > 0 {
|
|
dbQuerys := make([]*T, 0, len(dbs))
|
|
db := e.tx.WithContext(ctx).Where("id in (?)", dbs)
|
|
err := db.Find(&dbQuerys).Error
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager db query ids:%v error: %v", dbs, err)
|
|
return nil, err
|
|
}
|
|
for _, v := range dbQuerys {
|
|
resp = append(resp, v)
|
|
|
|
err = e.manager.Set(ctx, fmt.Sprintf("%d", e.idGetter(v)), v, e.ttl, []int64{e.idGetter(v)})
|
|
if err != nil {
|
|
e.options.logger.Errorf(ctx, "EventManager cache set id:%d error: %v", e.idGetter(v), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (l *EventManager[T]) GetByFields(ctx context.Context, fields map[string]interface{}) ([]*T, error) {
|
|
resp, err := l.manager.GetByFields(ctx, fields, l.ttl)
|
|
if err == nil {
|
|
return resp, nil
|
|
}
|
|
return l.manager.UpdateByFields(ctx, fields, l.ttl)
|
|
}
|
|
|
|
func (e *EventManager[T]) DeleteCacheById(ctx context.Context, id int64) error {
|
|
key := fmt.Sprintf("%d", id)
|
|
return e.manager.Del(ctx, key)
|
|
}
|
|
|
|
func (e *EventManager[T]) DeleteCacheByIds(ctx context.Context, ids []int64) error {
|
|
keys := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
keys = append(keys, fmt.Sprintf("%d", id))
|
|
}
|
|
return e.manager.BatchDel(ctx, keys)
|
|
}
|
|
|
|
func (e *EventManager[T]) DeleteCacheByFields(ctx context.Context, fields map[string]interface{}) error {
|
|
key := e.manager.fieldsToKey(fields)
|
|
return e.manager.Del(ctx, fmt.Sprintf("%v", key))
|
|
}
|
|
|
|
func (e *EventManager[T]) BatchDel(ctx context.Context, keys []string) error {
|
|
return e.manager.BatchDel(ctx, keys)
|
|
}
|