chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
130
internal/app/bots/profile_cache.go
Normal file
130
internal/app/bots/profile_cache.go
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
package bots
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/readmodelcache"
|
||||
)
|
||||
|
||||
const (
|
||||
botProfileCacheMaxEntries = 100000
|
||||
botProfileCacheTTL = 24 * time.Hour
|
||||
)
|
||||
|
||||
// botProfileValue 是缓存值,found=false 表示「查过但该 bot 不存在」(负缓存),
|
||||
// 避免未注册 bot 的 id 反复打后端。
|
||||
type botProfileValue struct {
|
||||
profile domain.BotProfile
|
||||
found bool
|
||||
}
|
||||
|
||||
// botProfileCache 由统一缓存原语承载(LRU 单条驱逐 / epoch 守卫 / clone 内建)。
|
||||
// 单个走 GetOrLoad,批量走 GetOrLoadBatch(一次 LoadEpoch + 合批 load + per-key epoch 写回)。
|
||||
type botProfileCache struct {
|
||||
cache *readmodelcache.Cache[int64, botProfileValue]
|
||||
}
|
||||
|
||||
func newBotProfileCache(max int, ttl time.Duration) *botProfileCache {
|
||||
cache := readmodelcache.New[int64, botProfileValue](readmodelcache.Config[int64, botProfileValue]{
|
||||
MaxEntries: max,
|
||||
TTL: ttl,
|
||||
Clone: cloneBotProfileValue,
|
||||
})
|
||||
if cache == nil {
|
||||
return nil
|
||||
}
|
||||
return &botProfileCache{cache: cache}
|
||||
}
|
||||
|
||||
// getOrLoad 解析单个 bot;load 返回 (profile, found, err)。
|
||||
func (c *botProfileCache) getOrLoad(ctx context.Context, botUserID int64, load func() (domain.BotProfile, bool, error)) (domain.BotProfile, bool, error) {
|
||||
if c == nil || botUserID == 0 {
|
||||
return load()
|
||||
}
|
||||
v, err := c.cache.GetOrLoad(ctx, botUserID, func() (botProfileValue, error) {
|
||||
profile, found, err := load()
|
||||
if err != nil {
|
||||
return botProfileValue{}, err
|
||||
}
|
||||
return botProfileValue{profile: normalizeBotProfile(botUserID, profile, found), found: found}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return domain.BotProfile{}, false, err
|
||||
}
|
||||
return v.profile, v.found, nil
|
||||
}
|
||||
|
||||
// getMany 批量解析;loadMissing 返回 misses 的 profiles(仅存在的;缺失即视为负结果)。
|
||||
// 返回的 map 只含存在(found)的 bot,与旧 getMany 语义一致。
|
||||
func (c *botProfileCache) getMany(ctx context.Context, ids []int64, loadMissing func(context.Context, []int64) (map[int64]domain.BotProfile, error)) (map[int64]domain.BotProfile, error) {
|
||||
unique := uniqueBotUserIDs(ids)
|
||||
if c == nil {
|
||||
return loadMissing(ctx, unique)
|
||||
}
|
||||
values, err := c.cache.GetOrLoadBatch(ctx, unique,
|
||||
func(int64) (int64, bool) { return 0, true }, // 纯 TTL,无版本闸门
|
||||
func(ctx context.Context, missing []int64) (map[int64]botProfileValue, error) {
|
||||
loaded, err := loadMissing(ctx, missing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[int64]botProfileValue, len(missing))
|
||||
for _, id := range missing {
|
||||
profile, found := loaded[id]
|
||||
out[id] = botProfileValue{profile: normalizeBotProfile(id, profile, found), found: found}
|
||||
}
|
||||
return out, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[int64]domain.BotProfile, len(values))
|
||||
for id, v := range values {
|
||||
if v.found {
|
||||
out[id] = v.profile
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *botProfileCache) put(botUserID int64, profile domain.BotProfile, found bool) {
|
||||
if c == nil || botUserID == 0 {
|
||||
return
|
||||
}
|
||||
c.cache.Store(botUserID, botProfileValue{profile: normalizeBotProfile(botUserID, profile, found), found: found})
|
||||
}
|
||||
|
||||
func (c *botProfileCache) delete(botUserID int64) {
|
||||
if c == nil || botUserID == 0 {
|
||||
return
|
||||
}
|
||||
c.cache.Invalidate(botUserID)
|
||||
}
|
||||
|
||||
func (c *botProfileCache) flush() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.cache.Flush()
|
||||
}
|
||||
|
||||
func normalizeBotProfile(botUserID int64, profile domain.BotProfile, found bool) domain.BotProfile {
|
||||
if found && profile.BotUserID == 0 {
|
||||
profile.BotUserID = botUserID
|
||||
}
|
||||
return profile
|
||||
}
|
||||
|
||||
func cloneBotProfileValue(v botProfileValue) botProfileValue {
|
||||
v.profile = cloneBotProfile(v.profile)
|
||||
return v
|
||||
}
|
||||
|
||||
func cloneBotProfile(profile domain.BotProfile) domain.BotProfile {
|
||||
if len(profile.Commands) > 0 {
|
||||
profile.Commands = append([]domain.BotCommand(nil), profile.Commands...)
|
||||
}
|
||||
return profile
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue