初始提交:OCI 面板后端(含 GenAI 网关一期)
This commit is contained in:
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
// Package cache 提供进程内 TTL 缓存与 singleflight 合并,服务 OCI 读放大
|
||||
// 与审计原始事件暂存等场景;单进程部署,无跨进程共享需求(调研①方案 A)。
|
||||
package cache
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type entry struct {
|
||||
val any
|
||||
exp time.Time
|
||||
}
|
||||
|
||||
// flight 是一次进行中的回源;后到的同 key 调用等待它完成。
|
||||
type flight struct {
|
||||
wg sync.WaitGroup
|
||||
val any
|
||||
err error
|
||||
}
|
||||
|
||||
// Cache 是带键数上限的进程内 TTL 缓存;并发 miss 同 key 只回源一次。
|
||||
type Cache struct {
|
||||
mu sync.Mutex
|
||||
items map[string]entry
|
||||
flights map[string]*flight
|
||||
max int
|
||||
}
|
||||
|
||||
// New 创建缓存;max 为键数上限,到限时先清过期项、仍满则任意驱逐。
|
||||
func New(max int) *Cache {
|
||||
return &Cache{items: map[string]entry{}, flights: map[string]*flight{}, max: max}
|
||||
}
|
||||
|
||||
// Get 返回未过期的缓存值。
|
||||
func (c *Cache) Get(key string) (any, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
e, ok := c.items[key]
|
||||
if !ok || time.Now().After(e.exp) {
|
||||
return nil, false
|
||||
}
|
||||
return e.val, true
|
||||
}
|
||||
|
||||
// Set 写入缓存;TTL 附加至多 1/10 的随机抖动,避免整批同时过期。
|
||||
func (c *Cache) Set(key string, val any, ttl time.Duration) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.items) >= c.max {
|
||||
c.evictLocked()
|
||||
}
|
||||
jitter := time.Duration(rand.Int64N(int64(ttl)/10 + 1))
|
||||
c.items[key] = entry{val: val, exp: time.Now().Add(ttl + jitter)}
|
||||
}
|
||||
|
||||
// evictLocked 先清全部过期项,仍到上限则任意删除腾出空位。
|
||||
func (c *Cache) evictLocked() {
|
||||
now := time.Now()
|
||||
for k, e := range c.items {
|
||||
if now.After(e.exp) {
|
||||
delete(c.items, k)
|
||||
}
|
||||
}
|
||||
for k := range c.items {
|
||||
if len(c.items) < c.max {
|
||||
return
|
||||
}
|
||||
delete(c.items, k)
|
||||
}
|
||||
}
|
||||
|
||||
// DeletePrefix 删除全部以 prefix 开头的键(写操作后按租户前缀失效)。
|
||||
func (c *Cache) DeletePrefix(prefix string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
for k := range c.items {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
delete(c.items, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// join 登记或加入 key 的进行中回源;返回 true 表示已有同 key 回源在跑。
|
||||
func (c *Cache) join(key string) (*flight, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if f, ok := c.flights[key]; ok {
|
||||
return f, true
|
||||
}
|
||||
f := &flight{}
|
||||
f.wg.Add(1)
|
||||
c.flights[key] = f
|
||||
return f, false
|
||||
}
|
||||
|
||||
// settle 结束回源登记并唤醒等待者。
|
||||
func (c *Cache) settle(key string, f *flight) {
|
||||
c.mu.Lock()
|
||||
delete(c.flights, key)
|
||||
c.mu.Unlock()
|
||||
f.wg.Done()
|
||||
}
|
||||
|
||||
// Do 返回缓存值;miss 时经 singleflight 回源,成功写缓存,错误不缓存
|
||||
// (等待者共享同一错误,下次调用重新回源)。
|
||||
func Do[T any](c *Cache, key string, ttl time.Duration, fn func() (T, error)) (T, error) {
|
||||
var zero T
|
||||
if v, ok := c.Get(key); ok {
|
||||
return v.(T), nil
|
||||
}
|
||||
f, joined := c.join(key)
|
||||
if joined {
|
||||
f.wg.Wait()
|
||||
if f.err != nil {
|
||||
return zero, f.err
|
||||
}
|
||||
return f.val.(T), nil
|
||||
}
|
||||
f.val, f.err = fn()
|
||||
c.settle(key, f)
|
||||
if f.err != nil {
|
||||
return zero, f.err
|
||||
}
|
||||
c.Set(key, f.val, ttl)
|
||||
return f.val.(T), nil
|
||||
}
|
||||
Reference in New Issue
Block a user