初始提交:OCI 面板后端(含 GenAI 网关一期)

This commit is contained in:
Wang Defa
2026-07-09 15:31:04 +08:00
commit b9a3e97e84
168 changed files with 31794 additions and 0 deletions
+129
View File
@@ -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
}