package face import ( "context" "encoding/json" "sync" "time" ) // CacheService defines the interface for caching face search results type CacheService interface { // Get retrieves a cached value by key Get(ctx context.Context, key string) (string, error) // Set stores a value with expiration Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error // Delete removes cached values Delete(ctx context.Context, keys ...string) error // GetJSON retrieves and unmarshals a cached JSON value GetJSON(ctx context.Context, key string, dest interface{}) error // SetJSON marshals and stores a JSON value SetJSON(ctx context.Context, key string, value interface{}, expiration time.Duration) error } // MemoryCache is an in-memory cache implementation for testing and development type MemoryCache struct { mu sync.RWMutex entries map[string]*cacheEntry } type cacheEntry struct { value string expiresAt time.Time } // NewMemoryCache creates a new in-memory cache func NewMemoryCache() *MemoryCache { return &MemoryCache{ entries: make(map[string]*cacheEntry), } } // Get retrieves a value from the cache func (c *MemoryCache) Get(ctx context.Context, key string) (string, error) { c.mu.RLock() defer c.mu.RUnlock() entry, ok := c.entries[key] if !ok { return "", nil } if time.Now().After(entry.expiresAt) { return "", nil } return entry.value, nil } // Set stores a value in the cache func (c *MemoryCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error { c.mu.Lock() defer c.mu.Unlock() var strValue string switch v := value.(type) { case string: strValue = v default: bytes, err := json.Marshal(v) if err != nil { return err } strValue = string(bytes) } c.entries[key] = &cacheEntry{ value: strValue, expiresAt: time.Now().Add(expiration), } return nil } // Delete removes values from the cache func (c *MemoryCache) Delete(ctx context.Context, keys ...string) error { c.mu.Lock() defer c.mu.Unlock() for _, key := range keys { delete(c.entries, key) } return nil } // GetJSON retrieves and unmarshals a cached JSON value func (c *MemoryCache) GetJSON(ctx context.Context, key string, dest interface{}) error { value, err := c.Get(ctx, key) if err != nil { return err } if value == "" { return nil } return json.Unmarshal([]byte(value), dest) } // SetJSON marshals and stores a JSON value func (c *MemoryCache) SetJSON(ctx context.Context, key string, value interface{}, expiration time.Duration) error { return c.Set(ctx, key, value, expiration) } // Cleanup removes expired entries (call periodically) func (c *MemoryCache) Cleanup() { c.mu.Lock() defer c.mu.Unlock() now := time.Now() for key, entry := range c.entries { if now.After(entry.expiresAt) { delete(c.entries, key) } } } // CacheKeys returns common cache key patterns type CacheKeys struct{} // Search returns the cache key for search results func (CacheKeys) Search(spaceID string, imageHash string) string { return "search:" + spaceID + ":" + imageHash } // Photo returns the cache key for photo metadata func (CacheKeys) Photo(photoID string) string { return "photo:" + photoID } // Face returns the cache key for face embeddings func (CacheKeys) Face(faceID string) string { return "face:" + faceID } // Indexed returns the cache key for indexed photos func (CacheKeys) Indexed(photoID string) string { return "indexed:" + photoID }