Implementing Thread-Safe In-Memory Cache in Go Part 2: Introducing Expiration to cache (TTL) (opens in new tab)

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),
}
}

Loading more...

Keyboard Shortcuts

Navigation
Next / previous item
j/k
Open post
oorEnter
Preview post
v
Post Actions
Love post
a
Like post
l
Dislike post
d
Undo reaction
u
Save / unsave
s
Recommendations
Add interest / feed
Enter
Not interested
x
Go to
Home
gh
Interests
gi
Feeds
gf
Likes
gl
History
gy
Changelog
gc
Settings
gs
Browse
gb
Search
/
General
Show this help
?
Submit feedback
!
Close modal / unfocus
Esc

Press ? anytime to show this help