Implement a Time-To-Live (TTL) Cache in JavaScript
Caching is a fundamental technique for improving application performance by storing frequently accessed data and serving it quickly. This challenge focuses on creating a cache that automatically purges expired entries, a common requirement for managing volatile data or preventing stale information from being served.
Problem Description
Your task is to implement a TTLCache class in JavaScript that stores key-value pairs with an associated Time-To-Live (TTL) duration. When a key is set, it should have a specified TTL. After this TTL expires, the entry associated with that key should be automatically removed from the cache and no longer retrievable.
Key Requirements:
-
set(key, value, ttl)method:- Adds or updates a key-value pair in the cache.
key: The identifier for the data.value: The data to be stored.ttl: The time-to-live in milliseconds.- If the key already exists, its value and TTL should be updated.
-
get(key)method:- Retrieves the value associated with a given
key. - If the
keyexists and its TTL has not expired, return thevalue. - If the
keydoes not exist or its TTL has expired, returnundefined.
- Retrieves the value associated with a given
-
Automatic Expiration:
- Entries should be removed from the cache automatically when their TTL expires. This should happen without explicit calls to a
deletemethod, ideally as a background process or upongetcalls.
- Entries should be removed from the cache automatically when their TTL expires. This should happen without explicit calls to a
-
Memory Management:
- Expired entries should be cleaned up to prevent memory leaks.
Expected Behavior:
- When
getis called for a key that has expired, it should returnundefined. - When
getis called for a key that is still valid, it should return its stored value. - When
setis called with a TTL, the item should be available forgetuntil that TTL elapses.
Edge Cases to Consider:
- Setting a TTL of 0 or a negative value should ideally result in the item being considered immediately expired or not cached at all (clarify this in your implementation).
- Handling concurrent
setandgetoperations (though for this challenge, a basic single-threaded JavaScript environment is assumed). - What happens if
setis called with a very large TTL?
Examples
Example 1: Basic Set and Get
const cache = new TTLCache();
cache.set('user:1', { name: 'Alice' }, 1000); // TTL of 1 second
console.log(cache.get('user:1')); // Output: { name: 'Alice' }
Explanation: The user data is set with a TTL of 1000 milliseconds. Immediately after, get retrieves the data successfully.
Example 2: Expired Entry
const cache = new TTLCache();
cache.set('session:abc', 'some_data', 500); // TTL of 0.5 seconds
// Wait for the TTL to expire
setTimeout(() => {
console.log(cache.get('session:abc')); // Output: undefined
}, 600);
Explanation: The 'session:abc' key is set with a short TTL. After the specified time (600ms, which is greater than 500ms), calling get returns undefined because the entry has expired.
Example 3: Updating an Entry and Expiration
const cache = new TTLCache();
cache.set('config', { setting1: 'value1' }, 2000); // Initial TTL of 2 seconds
console.log(cache.get('config')); // Output: { setting1: 'value1' }
// Update the entry with a new value and a shorter TTL
cache.set('config', { setting1: 'updated_value' }, 500);
console.log(cache.get('config')); // Output: { setting1: 'updated_value' }
// Wait for the shorter TTL to expire
setTimeout(() => {
console.log(cache.get('config')); // Output: undefined
}, 700);
Explanation: The 'config' key is first set. It's then updated with new data and a shorter TTL. The get call after the update retrieves the new data. After the shorter TTL expires, subsequent get calls return undefined.
Constraints
- The
TTLCacheclass should be implemented purely in JavaScript. - The TTL is measured in milliseconds.
- The cache should handle a reasonable number of entries (e.g., up to 10,000 entries without significant performance degradation).
- The underlying storage for the cache can be a JavaScript
MaporObject. - The implementation of automatic expiration should not block the main JavaScript thread excessively. Consider using
setTimeoutorsetIntervalcarefully.
Notes
- Think about how you will store not just the value, but also the expiration timestamp for each key.
- The
getmethod is a natural place to check for expired items. If an item is accessed and found to be expired, it can be removed at that moment. - For a more robust solution, consider implementing a background cleanup mechanism that periodically scans and removes expired items. This could involve
setInterval. - How will you handle the case where a TTL of 0 or less is provided? Decide on a consistent behavior.