package cache import ( "sync" "time" "fergalla.com/dockerbot/internal/docker" ) type ContainerCache struct { mu sync.RWMutex data []docker.ContainerBasicInfo expiresAt time.Time ttl time.Duration // función para obtener datos frescos fetchFunc func() ([]docker.ContainerBasicInfo, error) } // Constructor func NewContainerCache(ttl time.Duration, fetch func() ([]docker.ContainerBasicInfo, error)) *ContainerCache { return &ContainerCache{ ttl: ttl, fetchFunc: fetch, } } // Get devuelve datos cacheados o refresca si ha expirado func (c *ContainerCache) Get() ([]docker.ContainerBasicInfo, error) { // 🔍 lectura rápida c.mu.RLock() if time.Now().Before(c.expiresAt) && c.data != nil { data := c.data c.mu.RUnlock() return data, nil } c.mu.RUnlock() // 🔒 lock de escritura (refresco) c.mu.Lock() defer c.mu.Unlock() // ⚠️ doble check (evita refrescos duplicados) if time.Now().Before(c.expiresAt) && c.data != nil { return c.data, nil } data, err := c.fetchFunc() if err != nil { return nil, err } c.data = data c.expiresAt = time.Now().Add(c.ttl) return c.data, nil } // Invalidate fuerza limpieza de cache func (c *ContainerCache) Invalidate() { c.mu.Lock() defer c.mu.Unlock() c.data = nil c.expiresAt = time.Time{} } // ForceRefresh fuerza actualización inmediata func (c *ContainerCache) ForceRefresh() ([]docker.ContainerBasicInfo, error) { c.mu.Lock() defer c.mu.Unlock() data, err := c.fetchFunc() if err != nil { return nil, err } c.data = data c.expiresAt = time.Now().Add(c.ttl) return c.data, nil }