Files
rond-with-you/internal/web/mask_cache.go
T
2026-09-18 13:05:49 +08:00

85 lines
2.4 KiB
Go

package web
import (
"sync"
"time"
"rond-with-you/internal/fog"
)
// maskTTL 是遮罩列表的缓存时长。保存/删除遮罩时会主动失效,这里的 TTL 只是兜底
// (比如多实例部署时另一个进程改了库)。所以可以取长一点。
const maskTTL = 30 * time.Second
// maskCache 缓存迷雾遮罩列表。
//
// 为什么需要:遮罩不在 fogIndex 的内存索引里,每个迷雾瓦片请求都会查一次库,
// 而地图一屏就是几十张瓦片——同一份数据被反复查几十遍。指纹也顺带缓存,
// 它被瓦片 URL 与缓存键用到。
type maskCache struct {
mu sync.Mutex
at time.Time
list []fog.Mask
fp string
valid bool
}
// get 返回缓存的遮罩列表与指纹;ok 为 false 表示需要查库。
// 「站点一块遮罩都没有」也是有效缓存(list 为 nil),所以不能拿 list 判空。
func (c *maskCache) get() (list []fog.Mask, fp string, ok bool) {
c.mu.Lock()
defer c.mu.Unlock()
if c.valid && time.Since(c.at) < maskTTL {
return c.list, c.fp, true
}
return nil, "", false
}
// put 写入缓存(由调用方查库后调用)。
func (c *maskCache) put(list []fog.Mask) ([]fog.Mask, string) {
fp := fog.MaskFingerprint(list)
c.mu.Lock()
c.list, c.fp, c.at, c.valid = list, fp, time.Now(), true
c.mu.Unlock()
return list, fp
}
// invalidate 在遮罩被保存 / 删除后立刻失效,保证改完立即对所有访客生效。
func (c *maskCache) invalidate() {
c.mu.Lock()
c.valid = false
c.mu.Unlock()
}
// masksFor 取遮罩列表(带缓存)。查库失败时返回 nil(等于不遮),与原先的行为一致。
func (s *Server) masksFor() []fog.Mask {
if list, _, ok := s.masks.get(); ok {
return list
}
list, err := fog.LoadMasks(s.db)
if err != nil {
return nil
}
out, _ := s.masks.put(list)
return out
}
// maskSetFor 取遮罩判定集合(带缓存);没有遮罩时返回 nil。
//
// admin 为 true 时不返回遮罩——站长永远看完整迷雾,否则没法核对遮哪里。
func (s *Server) maskSetFor(admin bool) *fog.MaskSet {
if admin {
return nil
}
return fog.NewMaskSet(s.masksFor())
}
// maskFP 取遮罩指纹(带缓存),供瓦片 URL 与缓存键使用。
func (s *Server) maskFP() string {
if _, fp, ok := s.masks.get(); ok {
return fp
}
list := s.masksFor()
return fog.MaskFingerprint(list)
}