package db import ( "sync" "time" ) type entityCacheEntry struct { slug string id string attrs string exp time.Time } // EntityCache is a TTL cache mapping entity IDs to slugs and back, // keyed for the hot resolution paths. type EntityCache struct { mu sync.RWMutex m map[string]entityCacheEntry ttl time.Duration } // NewEntityCache builds a cache with the given TTL. func NewEntityCache(ttl time.Duration) *EntityCache { return &EntityCache{ m: make(map[string]entityCacheEntry), ttl: ttl, } } // GetSlug resolves an entity ID to its slug. func (c *EntityCache) GetSlug(id string) (string, bool) { c.mu.RLock() e, ok := c.m[id] c.mu.RUnlock() if !ok || time.Now().After(e.exp) { return "", false } return e.slug, true } // GetID resolves a slug to its entity ID. func (c *EntityCache) GetID(slug string) (string, bool) { c.mu.RLock() e, ok := c.m[slug] c.mu.RUnlock() if !ok || time.Now().After(e.exp) { return "", false } return e.id, true } // Set records the slug/id pair and serialized attributes. func (c *EntityCache) Set(slug, id, attrs string) { exp := time.Now().Add(c.ttl) c.mu.Lock() c.m[slug] = entityCacheEntry{slug: slug, id: id, attrs: attrs, exp: exp} c.m[id] = entityCacheEntry{slug: slug, id: id, attrs: attrs, exp: exp} c.mu.Unlock() } // Invalidate drops the cached entries for one slug/id pair. func (c *EntityCache) Invalidate(slug, id string) { c.mu.Lock() delete(c.m, slug) delete(c.m, id) c.mu.Unlock() }