package cache import ( "container/list" "context" "sync" "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" ) type lruEntry struct { key string response *CachedResponse expiresAt time.Time } type memoryResponseCache struct { cfg config.ResponseCacheConfig mu sync.RWMutex items map[string]*list.Element eviction *list.List stats responseCacheStats } func newMemoryResponseCache(cfg config.ResponseCacheConfig) *memoryResponseCache { maxEntries := cfg.MaxMemoryEntries if maxEntries <= 0 { maxEntries = 1000 } return &memoryResponseCache{ cfg: cfg, items: make(map[string]*list.Element, maxEntries), eviction: list.New(), } } func (m *memoryResponseCache) Get(_ context.Context, key string) (*CachedResponse, bool) { m.mu.RLock() elem, ok := m.items[key] m.mu.RUnlock() if !ok { m.stats.recordMiss() return nil, false } entry := elem.Value.(*lruEntry) if time.Now().After(entry.expiresAt) { // Lazy expiry m.mu.Lock() m.removeElement(elem) m.mu.Unlock() m.stats.recordMiss() return nil, false } // Move to front (most recently used) m.mu.Lock() m.eviction.MoveToFront(elem) m.mu.Unlock() m.stats.recordHit() return entry.response, true } func (m *memoryResponseCache) Set(_ context.Context, key string, resp *CachedResponse, ttl time.Duration) error { m.mu.Lock() defer m.mu.Unlock() // Update existing if elem, ok := m.items[key]; ok { entry := elem.Value.(*lruEntry) entry.response = resp entry.expiresAt = time.Now().Add(ttl) m.eviction.MoveToFront(elem) return nil } maxEntries := m.cfg.MaxMemoryEntries if maxEntries <= 0 { maxEntries = 1000 } // Evict if at capacity for len(m.items) >= maxEntries && m.eviction.Len() > 0 { back := m.eviction.Back() if back != nil { m.removeElement(back) } } // Add new entry := &lruEntry{ key: key, response: resp, expiresAt: time.Now().Add(ttl), } elem := m.eviction.PushFront(entry) m.items[key] = elem return nil } func (m *memoryResponseCache) Delete(_ context.Context, _ string) error { m.mu.Lock() defer m.mu.Unlock() m.items = make(map[string]*list.Element) m.eviction.Init() return nil } func (m *memoryResponseCache) Stats() CacheStats { m.mu.RLock() size := int64(len(m.items)) m.mu.RUnlock() return CacheStats{ Hits: m.stats.hits.Load(), Misses: m.stats.misses.Load(), Size: size, } } func (m *memoryResponseCache) Close() error { return nil } func (m *memoryResponseCache) removeElement(elem *list.Element) { entry := elem.Value.(*lruEntry) delete(m.items, entry.key) m.eviction.Remove(elem) }