package main import ( "fmt" "sync" "time" ) // Cache represents a simple in-memory cache. type Cache struct { data map[string]string mu sync.RWMutex } func NewCache() *Cache { return &Cache{data: make(map[string]string)} } func (c *Cache) Get(key string) (string, bool) { c.mu.RLock() defer c.mu.RUnlock() val, ok := c.data[key] return val, ok } func (c *Cache) Set(key, value string) { c.mu.Lock() defer c.mu.Unlock() c.data[key] = value fmt.Printf("Cache: Set %s = %s ", key, value) } func (c *Cache) Invalidate(key string) { c.mu.Lock() defer c.mu.Unlock() delete(c.data, key) fmt.Printf("Cache: Invalidated key %s ", key) } // CacheManager listens for invalidation signals and updates the cache. func CacheManager(cache *Cache, invalidateSignal <-chan string, stopSignal <-chan struct{}) { fmt.Println("CacheManager: Started listening for invalidation signals.") for { select { case keyToInvalidate := <-invalidateSignal: cache.Invalidate(keyToInvalidate) // In a real scenario, you might also trigger an asynchronous refresh // go cache.Refresh(keyToInvalidate) case <-stopSignal: fmt.Println("CacheManager: Received stop signal. Shutting down.") return } } } func main() { myCache := NewCache() invalidateCh := make(chan string) stopCh := make(chan struct{}) go CacheManager(myCache, invalidateCh, stopCh) // Simulate initial data loading myCache.Set("user:1", "Alice") myCache.Set("product:101", "Laptop") // Simulate web request accessing cache fmt.Println(" Simulating cache access:") val, ok := myCache.Get("user:1") if ok { fmt.Printf("Retrieved from cache: user:1 = %s ", val) } time.Sleep(500 * time.Millisecond) // Simulate an update in the database and signal cache invalidation fmt.Println(" Simulating database update and cache invalidation signal:") invalidateCh <- "user:1" time.Sleep(100 * time.Millisecond) // Give manager time to process // Try accessing again after invalidation fmt.Println(" Simulating cache access after invalidation:") val, ok = myCache.Get("user:1") if !ok { fmt.Println("user:1 not found in cache (successfully invalidated).") // In a real app, you'd now fetch from DB and re-cache myCache.Set("user:1", "Alice (Updated)") } time.Sleep(1 * time.Second) // Stop the CacheManager goroutine close(stopCh) fmt.Println(" Main: Sent stop signal to CacheManager.") // Give time for shutdown to complete time.Sleep(200 * time.Millisecond) fmt.Println("Main: Finished.") }