Files
2025-10-17 20:40:04 -05:00

99 lines
2.3 KiB
Go

package storage
import (
"sync"
"time"
)
// HoldCache caches hold DIDs for (DID, repository) pairs
// This avoids expensive ATProto lookups on every blob request during pulls
//
// NOTE: This is a simple in-memory cache for MVP. For production deployments:
// - Use Redis or similar for distributed caching
// - Consider implementing cache size limits
// - Monitor memory usage under high load
type HoldCache struct {
mu sync.RWMutex
cache map[string]*holdCacheEntry
}
type holdCacheEntry struct {
holdDID string
expiresAt time.Time
}
var globalHoldCache = &HoldCache{
cache: make(map[string]*holdCacheEntry),
}
func init() {
// Start background cleanup goroutine
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
globalHoldCache.Cleanup()
}
}()
}
// GetGlobalHoldCache returns the global hold cache instance
func GetGlobalHoldCache() *HoldCache {
return globalHoldCache
}
// Set stores a hold DID for a (DID, repository) pair with a TTL
func (c *HoldCache) Set(did, repository, holdDID string, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
key := did + ":" + repository
c.cache[key] = &holdCacheEntry{
holdDID: holdDID,
expiresAt: time.Now().Add(ttl),
}
}
// Get retrieves a hold DID for a (DID, repository) pair
// Returns empty string and false if not found or expired
func (c *HoldCache) Get(did, repository string) (string, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
key := did + ":" + repository
entry, ok := c.cache[key]
if !ok {
return "", false
}
// Check if expired
if time.Now().After(entry.expiresAt) {
// Don't delete here (would need write lock), let cleanup handle it
return "", false
}
return entry.holdDID, true
}
// Cleanup removes expired entries (called automatically every 5 minutes)
func (c *HoldCache) Cleanup() {
c.mu.Lock()
defer c.mu.Unlock()
now := time.Now()
removed := 0
for key, entry := range c.cache {
if now.After(entry.expiresAt) {
delete(c.cache, key)
removed++
}
}
// Log cleanup stats for monitoring
if removed > 0 || len(c.cache) > 100 {
// Log if we removed entries OR if cache is growing large
// This helps identify if cache size is becoming a concern
println("Hold cache cleanup: removed", removed, "entries, remaining", len(c.cache))
}
}