package cache import ( "context" "sync" "time" ) type memoryEntry struct { data []byte expiresAt time.Time } type memoryCache struct { mu sync.RWMutex entries map[string]memoryEntry } func newMemoryCache() Cache { c := &memoryCache{entries: make(map[string]memoryEntry)} go c.evictLoop() return c } func (c *memoryCache) Get(_ context.Context, key string) ([]byte, error) { c.mu.RLock() entry, ok := c.entries[key] c.mu.RUnlock() if !ok || time.Now().After(entry.expiresAt) { if ok { c.mu.Lock() delete(c.entries, key) c.mu.Unlock() } return nil, ErrNotFound } dst := make([]byte, len(entry.data)) copy(dst, entry.data) return dst, nil } func (c *memoryCache) Set(_ context.Context, key string, value []byte, ttl time.Duration) error { dst := make([]byte, len(value)) copy(dst, value) c.mu.Lock() c.entries[key] = memoryEntry{data: dst, expiresAt: time.Now().Add(ttl)} c.mu.Unlock() return nil } func (c *memoryCache) Delete(_ context.Context, key string) error { c.mu.Lock() delete(c.entries, key) c.mu.Unlock() return nil } func (c *memoryCache) evictLoop() { ticker := time.NewTicker(60 * time.Second) defer ticker.Stop() for range ticker.C { now := time.Now() c.mu.Lock() for k, v := range c.entries { if now.After(v.expiresAt) { delete(c.entries, k) } } c.mu.Unlock() } }