Download backend/internal/pkg/resultcache/cache.go from li2895/li: direct link, hf CLI and curl.
- Browser
- Download file 2.88 kB
-
https://huggingface.co/spaces/li2895/li/resolve/main/backend/internal/pkg/resultcache/cache.go
- Command line
-
hf download hf://spaces/li2895/li/backend/internal/pkg/resultcache/cache.go
-
curl -L -o cache.go https://huggingface.co/spaces/li2895/li/resolve/main/backend/internal/pkg/resultcache/cache.go
2.88 kB
| package resultcache | |
| import ( | |
| "context" | |
| "errors" | |
| "sync" | |
| "time" | |
| ) | |
| type entry[V any] struct { | |
| value V | |
| expiresAt time.Time | |
| storedAt time.Time | |
| } | |
| type flight[V any] struct { | |
| done chan struct{} | |
| value V | |
| err error | |
| } | |
| // Cache 保存少量短生命周期计算结果;达到容量时淘汰最早写入的条目。 | |
| type Cache[K comparable, V any] struct { | |
| mu sync.Mutex | |
| ttl time.Duration | |
| maxSize int | |
| values map[K]entry[V] | |
| loads map[K]*flight[V] | |
| } | |
| func New[K comparable, V any](maxSize int, ttl time.Duration) *Cache[K, V] { | |
| if maxSize < 1 { | |
| maxSize = 1 | |
| } | |
| if ttl <= 0 { | |
| ttl = time.Second | |
| } | |
| return &Cache[K, V]{ttl: ttl, maxSize: maxSize, values: make(map[K]entry[V], maxSize), loads: make(map[K]*flight[V])} | |
| } | |
| func (c *Cache[K, V]) Get(key K, now time.Time) (V, bool) { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| value, ok := c.values[key] | |
| if !ok { | |
| var zero V | |
| return zero, false | |
| } | |
| if !now.Before(value.expiresAt) { | |
| delete(c.values, key) | |
| var zero V | |
| return zero, false | |
| } | |
| return value.value, true | |
| } | |
| // Load 合并同一键的并发加载;等待者可独立取消,不受首个请求生命周期拖累。 | |
| func (c *Cache[K, V]) Load(ctx context.Context, key K, now time.Time, loader func() (V, error)) (V, error) { | |
| if value, ok := c.Get(key, now); ok { | |
| return value, nil | |
| } | |
| c.mu.Lock() | |
| if pending, ok := c.loads[key]; ok { | |
| c.mu.Unlock() | |
| select { | |
| case <-pending.done: | |
| return pending.value, pending.err | |
| case <-ctx.Done(): | |
| var zero V | |
| return zero, ctx.Err() | |
| } | |
| } | |
| pending := &flight[V]{done: make(chan struct{})} | |
| c.loads[key] = pending | |
| c.mu.Unlock() | |
| defer func() { | |
| if recovered := recover(); recovered != nil { | |
| c.mu.Lock() | |
| pending.err = errors.New("缓存加载异常中断") | |
| delete(c.loads, key) | |
| close(pending.done) | |
| c.mu.Unlock() | |
| panic(recovered) | |
| } | |
| }() | |
| pending.value, pending.err = loader() | |
| c.mu.Lock() | |
| if pending.err == nil { | |
| c.setLocked(key, pending.value, now) | |
| } | |
| delete(c.loads, key) | |
| close(pending.done) | |
| c.mu.Unlock() | |
| return pending.value, pending.err | |
| } | |
| func (c *Cache[K, V]) Set(key K, value V, now time.Time) { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| c.setLocked(key, value, now) | |
| } | |
| // Delete 失效指定键的已缓存结果;正在执行的加载不被中断。 | |
| func (c *Cache[K, V]) Delete(key K) { | |
| c.mu.Lock() | |
| defer c.mu.Unlock() | |
| delete(c.values, key) | |
| } | |
| func (c *Cache[K, V]) setLocked(key K, value V, now time.Time) { | |
| if _, exists := c.values[key]; !exists && len(c.values) >= c.maxSize { | |
| var oldestKey K | |
| var oldestAt time.Time | |
| found := false | |
| for candidateKey, candidate := range c.values { | |
| if !found || candidate.storedAt.Before(oldestAt) { | |
| oldestKey, oldestAt, found = candidateKey, candidate.storedAt, true | |
| } | |
| } | |
| if found { | |
| delete(c.values, oldestKey) | |
| } | |
| } | |
| c.values[key] = entry[V]{value: value, expiresAt: now.Add(c.ttl), storedAt: now} | |
| } | |