chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -0,0 +1,88 @@
package postgres
import (
"context"
"telesrv/internal/domain"
"telesrv/internal/readmodelcache"
)
// ChannelRowCache 是「共享频道行」(channels 表一行 → domain.Channel) 的进程内强一致缓存,
// 由统一缓存原语 readmodelcache.Cache 承载(LRU 单条驱逐 / epoch 守卫 / singleflight 内建)。
//
// 只缓存全 viewer 通用、变更不频繁的频道行per-viewer 的 member(权限/读态) 由
// ChannelMemberCache 以 (channel_id,user_id) 单独隔离,dialog(top/未读)、boost 仍实时查 PG。
//
// 一致性靠统一 read-model PG LISTEN/NOTIFY 实时失效(见 ReadModelChangeListener)而非 TTL
// channels 表上的 read-model 触发器在提交时发布 channel_base 事件,listener 收到即 delete
// 对应条目listener (重)连接时整表 flush,兜住断连窗口内丢失的通知。无 TTL,故 epoch 守卫
// 是防永久 stale 的唯一保障(已内建于原语)。
//
// 仅在连接池句柄(非事务)上消费(见 ChannelStore.cacheActive):事务内一律实时读,保证读己写。
type ChannelRowCache struct {
cache *readmodelcache.Cache[int64, domain.Channel]
}
// NewChannelRowCache 创建容量为 max 的频道行缓存max<=0 返回 nil(表示禁用,调用方按 nil 跳过)。
func NewChannelRowCache(max int) *ChannelRowCache {
cache := readmodelcache.New[int64, domain.Channel](readmodelcache.Config[int64, domain.Channel]{
MaxEntries: max,
Clone: cloneChannelRow,
})
if cache == nil {
return nil
}
return &ChannelRowCache{cache: cache}
}
func (c *ChannelRowCache) get(id int64) (domain.Channel, bool) {
if c == nil || id == 0 {
return domain.Channel{}, false
}
return c.cache.Peek(id)
}
func (c *ChannelRowCache) getOrLoad(ctx context.Context, id int64, load func() (domain.Channel, error)) (domain.Channel, error) {
if c == nil {
return load()
}
return c.cache.GetOrLoad(ctx, id, load)
}
func (c *ChannelRowCache) put(ch domain.Channel) {
if c == nil || ch.ID == 0 {
return
}
c.cache.Store(ch.ID, ch)
}
// delete 失效单个频道(listener 收到该 id 的变更通知时调用)。
func (c *ChannelRowCache) delete(id int64) {
if c == nil || id == 0 {
return
}
c.cache.Invalidate(id)
}
// flush 清空整表(listener 每次 (重)连接、建立 LISTEN 后调用,兜住断连窗口内丢失的通知)。
func (c *ChannelRowCache) flush() {
if c == nil {
return
}
c.cache.Flush()
}
// cloneChannelRow 深拷频道行的可变字段(切片),使缓存与调用方互不别名污染。
func cloneChannelRow(ch domain.Channel) domain.Channel {
if ch.PhotoStripped != nil {
ch.PhotoStripped = append([]byte(nil), ch.PhotoStripped...)
}
if ch.ReactionPolicy.Emoticons != nil {
ch.ReactionPolicy.Emoticons = append([]string(nil), ch.ReactionPolicy.Emoticons...)
}
if ch.ReactionPolicy.CustomEmojiIDs != nil {
ch.ReactionPolicy.CustomEmojiIDs = append([]int64(nil), ch.ReactionPolicy.CustomEmojiIDs...)
}
ch.Wallpaper = domain.CloneWallpaperPtr(ch.Wallpaper)
return ch
}