In this lesson, you will learn how to implement automated cleanup tasks in your Node.js application to prevent memory leaks and keep your cache footprint stable.
Why Caches Need Housekeeping
When you build a cache, you often focus on getting data out of it quickly. However, without an automated removal strategy, your cache will grow indefinitely as new keys are added, eventually leading to heap out of memory errors. A self-cleaning cache periodically reconciles its memory usage by pruning stale or expired entries without human intervention.
Building a "Janitor" Process
In Node.js, we don't want the main request thread to be responsible for deleting thousands of keys, as this would cause significant latency spikes. Instead, we implement a "janitor" pattern using setInterval or a scheduled background task that handles the cleanup.
Here is a simplified implementation of a self-cleaning cache mechanism:
class SelfCleaningCache {
constructor(ttlMs = 60000) {
this.cache = new Map();
// Run cleanup every 30 seconds
setInterval(() => this.cleanup(), 30000).unref();
}
cleanup() {
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expiry) {
this.cache.delete(key);
}
}
}
set(key, value, ttl) {
this.cache.set(key, { value, expiry: Date.now() + ttl });
}
}
The Role of unref()
Notice the use of .unref() on the setInterval timer. This is a crucial Node.js best practice: it prevents the timer from keeping the Node.js process alive if your application is trying to shut down. The background task runs independently, efficiently pruning your data structure while keeping the main loop responsive to incoming traffic.
By offloading the expiration logic to a dedicated background task, you ensure that your cache size remains bounded by your business logic (the TTL) rather than the total number of requests received. This proactive maintenance is the key to preventing the "memory spikes" we discussed in previous lessons.
Key Takeaway: A robust cache must pair its retrieval logic with a background janitor process that periodically prunes stale data to maintain a predictable, stable memory footprint.