Files
cache_manager/manager/event_manager.go
T

182 lines
5.2 KiB
Go
Raw Normal View History

2026-05-16 19:54:12 +08:00
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)
}