Hello, I’m Ganesh. I’m working on FreeDevTools online, currently building a single platform for all development tools, cheat codes, and TL; DRs — a free, open-source hub where developers can quickly find and use tools without the hassle of searching the internet.
In the previous article, we learnt how cache will help in improving performance and efficiency, and how to implement a simple in-memory cache. If you have not read it, you can check it out here link.
The previous implementation did not handle expiration, which is crucial for any application. Now, let’s modify the existing code to store the TTL (Time To Live).
Core Logic Behind Cache with Expiration (TTL)
Since each cache entry can have a defined TTL, we create a CacheItem struct that contains interface{} for storing value and an expiry time field.
1. The Structure
Instead of storing just raw data, we now wrap every value in a CacheItem struct:
type CacheItem struct {
value interface{} // The actual data (e.g., "mohit", 75)
expiry time.Time // The exact timestamp when this data dies (e.g., 10:05:00 PM)
}
The Cache map now stores string -> CacheItem.
2. How it Works (Logic)
Adding Data When you add data, you must specify how long it should live. Expiry Time = Current Time + TTL
Retrieving Data This is the smartest part. The cache does not run a background timer to delete old keys (which consumes CPU). Instead, it checks at the moment you ask for the data.
Lookup: It finds the item in the map. Check: Is Current Time > Expiry Time? If Yes (Expired): It explicitly deletes the item right now and tells you it wasn’t found (false). If No (Valid): It returns the value.
Implementing Thread-Safe In-Memory Cache with Expiration (TTL)
We just need to update the cache structure, set function, and get function.
package main
import (
"fmt"
"sync"
"time"
)
// CacheItem represents an item stored in the cache with its associated TTL.
type CacheItem struct {
value interface{}
expiry time.Time // TTL for a key
}
// Cache represents an in-memory key-value store with expiry support.
type Cache struct {
data map[string]CacheItem
mu sync.RWMutex
}
// NewCache creates and initializes a new Cache instance.
func NewCache() *Cache {
return &Cache{
data: make(map[string]CacheItem),
}
}
// Set adds or updates a key-value pair in the cache with the given TTL.
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = CacheItem{
value: value,
expiry: time.Now().Add(ttl),
}
}