Hone logo
Hone
Problems

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:

  1. 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.
  2. get(key) method:

    • Retrieves the value associated with a given key.
    • If the key exists and its TTL has not expired, return the value.
    • If the key does not exist or its TTL has expired, return undefined.
  3. Automatic Expiration:

    • Entries should be removed from the cache automatically when their TTL expires. This should happen without explicit calls to a delete method, ideally as a background process or upon get calls.
  4. Memory Management:

    • Expired entries should be cleaned up to prevent memory leaks.

Expected Behavior:

  • When get is called for a key that has expired, it should return undefined.
  • When get is called for a key that is still valid, it should return its stored value.
  • When set is called with a TTL, the item should be available for get until 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 set and get operations (though for this challenge, a basic single-threaded JavaScript environment is assumed).
  • What happens if set is 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 TTLCache class 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 Map or Object.
  • The implementation of automatic expiration should not block the main JavaScript thread excessively. Consider using setTimeout or setInterval carefully.

Notes

  • Think about how you will store not just the value, but also the expiration timestamp for each key.
  • The get method 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.
Loading editor...
javascript