Redis Cache Helper Class in Python with TTL
Build a DataHelper class that caches function results in Redis with a default TTL, using get_or_set and clear methods.
pip install redis
Python code
40 linesimport redis
import json
import time
class DataHelper:
def __init__(self, host="localhost", port=6379, db=0, default_ttl=60):
self.client = redis.Redis(host=host, port=port, db=db, decode_responses=True)
self.default_ttl = default_ttl
def get_or_set(self, key, data_func, ttl=None):
cached = self.client.get(key)
if cached is not None:
return json.loads(cached)
fresh_data = data_func()
ttl = ttl or self.default_ttl
self.client.setex(key, ttl, json.dumps(fresh_data))
return fresh_data
def clear(self, key):
return self.client.delete(key)
if __name__ == "__main__":
helper = DataHelper(default_ttl=5)
def expensive_data():
return {"timestamp": time.time(), "items": [1, 2, 3]}
first = helper.get_or_set("demo:data", expensive_data)
print(f"First call: {first}")
second = helper.get_or_set("demo:data", expensive_data)
print(f"Second call (cached): {second}")
print(f"Values equal: {first == second}")
helper.clear("demo:data")
third = helper.get_or_set("demo:data", expensive_data)
print(f"After clear — new value: {third}")
Output
First call: {'timestamp': 1713123456.78, 'items': [1, 2, 3]}
Second call (cached): {'timestamp': 1713123456.78, 'items': [1, 2, 3]}
Values equal: True
After clear — new value: {'timestamp': 1713123456.99, 'items': [1, 2, 3]}
How it works
The DataHelper class wraps a Redis client with decode_responses=True so cached values are returned as strings, not bytes. get_or_set first checks if the key exists; if it does, it returns the decoded JSON. Otherwise it calls data_func, serializes the result with json.dumps, and stores it with setex using the TTL (default 60 seconds, overridden here to 5). This pattern is a classic cache-aside implementation: check cache, on miss fetch the source and populate the cache. The clear method deletes the key so the next call triggers a fresh computation. Using JSON as the serialization format keeps the cache human-readable and works well for dict/list structures.
Common mistakes
- Forgetting to set `decode_responses=True` leads to bytes objects and errors when comparing with strings.
- Not using `setex` and manually setting expiration with `set` + `expire` — `setex` is atomic and simpler.
- Assuming cached data is always fresh; TTL might be too long for frequently changing data.
Variations
- Use `pickle` instead of `json` for complex Python objects, although it's less portable.
- Add a `get_or_set` with a callable that accepts extra arguments by using `functools.partial`.
Real-world use cases
- Caching database query results behind an API endpoint to reduce latency and load.
- Storing expensive computed or fetched data (e.g., third-party API calls) with a short TTL.
- Implementing a simple session or config store where keys map to JSON objects.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.