package chatcore import ( "context" "encoding/json" "fmt" "time" "github.com/redis/go-redis/v9" ) const ( msgCachePrefix = "chat:msgs:" msgCacheLimit = 100 msgCacheTTL = 30 * time.Minute ) type CachedStore struct { inner Store rdb redis.UniversalClient } func NewCachedStore(inner Store, rdb redis.UniversalClient) *CachedStore { return &CachedStore{inner: inner, rdb: rdb} } func msgKey(sessionId int64) string { return fmt.Sprintf("%s%d", msgCachePrefix, sessionId) } func (c *CachedStore) CreateSession(typ SessionType, name string, creatorId int64, participants []int64) (*Session, error) { return c.inner.CreateSession(typ, name, creatorId, participants) } func (c *CachedStore) GetSession(id int64) (*Session, error) { return c.inner.GetSession(id) } func (c *CachedStore) ListUserSessions(userId int64, page, limit int) []*Session { return c.inner.ListUserSessions(userId, page, limit) } func (c *CachedStore) AddParticipant(sessionId, userId int64) error { return c.inner.AddParticipant(sessionId, userId) } func (c *CachedStore) RemoveParticipant(sessionId, userId int64) error { return c.inner.RemoveParticipant(sessionId, userId) } func (c *CachedStore) DeleteSession(id int64) { c.inner.DeleteSession(id) c.rdb.Del(context.Background(), msgKey(id)) } func (c *CachedStore) AddMessage(sessionId, senderId int64, msgType MessageType, content string) (*Message, error) { msg, err := c.inner.AddMessage(sessionId, senderId, msgType, content) if err != nil { return nil, err } data, _ := json.Marshal(msg) ctx := context.Background() key := msgKey(sessionId) pipe := c.rdb.Pipeline() pipe.RPush(ctx, key, data) pipe.LTrim(ctx, key, -msgCacheLimit, -1) pipe.Expire(ctx, key, msgCacheTTL) pipe.Exec(ctx) return msg, nil } func (c *CachedStore) GetMessages(sessionId int64, page, limit int) []*Message { if page == 0 && limit <= msgCacheLimit { if msgs := c.getFromCache(sessionId, limit); msgs != nil { return msgs } } msgs := c.inner.GetMessages(sessionId, page, limit) if page == 0 && len(msgs) > 0 { c.warmCache(sessionId, msgs) } return msgs } func (c *CachedStore) getFromCache(sessionId int64, limit int) []*Message { ctx := context.Background() key := msgKey(sessionId) exists, err := c.rdb.Exists(ctx, key).Result() if err != nil || exists == 0 { return nil } vals, err := c.rdb.LRange(ctx, key, 0, int64(limit)-1).Result() if err != nil || len(vals) == 0 { return nil } msgs := make([]*Message, 0, len(vals)) for _, v := range vals { var msg Message if json.Unmarshal([]byte(v), &msg) == nil { msgs = append(msgs, &msg) } } return msgs } func (c *CachedStore) warmCache(sessionId int64, msgs []*Message) { ctx := context.Background() key := msgKey(sessionId) pipe := c.rdb.Pipeline() pipe.Del(ctx, key) for _, msg := range msgs { data, _ := json.Marshal(msg) pipe.RPush(ctx, key, data) } pipe.LTrim(ctx, key, -msgCacheLimit, -1) pipe.Expire(ctx, key, msgCacheTTL) pipe.Exec(ctx) }