Redis GET SET EX TTL mock in Python
A thread-safe Python class mimicking Redis GET, SET with EX, and TTL commands for in-memory testing.
Python code
48 linesimport time
import threading
from typing import Optional, Callable
class RedisTTLMock:
def __init__(self):
self._store: dict[str, tuple[str, float]] = {}
self._lock = threading.Lock()
def set(self, key: str, value: str, ex: Optional[int] = None) -> bool:
expiry = time.time() + ex if ex is not None else float("inf")
with self._lock:
self._store[key] = (value, expiry)
return True
def get(self, key: str) -> Optional[str]:
with self._lock:
entry = self._store.get(key)
if entry is None:
return None
value, expiry = entry
if time.time() > expiry:
del self._store[key]
return None
return value
def ttl(self, key: str) -> int:
with self._lock:
entry = self._store.get(key)
if entry is None:
return -2
_, expiry = entry
remaining = expiry - time.time()
if remaining <= 0:
del self._store[key]
return -2
return int(remaining)
if __name__ == "__main__":
r = RedisTTLMock()
r.set("greeting", "hello", ex=2)
print(r.get("greeting")) # hello
print(r.ttl("greeting")) # 2
time.sleep(3)
print(r.get("greeting")) # None
print(r.ttl("greeting")) # -2
Output
hello
2
None
-2
How it works
This mock uses a dictionary to store each key's value and expiry timestamp. The set method stores the value and calculates an expiration time using time.time() + ex (or infinity if no TTL). get checks the expiry before returning the value; expired keys are deleted to keep the store clean. TTL returns -2 for missing keys, -1 for keys without expiry (infinity), and the remaining seconds otherwise. A lock ensures thread safety for concurrent access.
Common mistakes
- Forgetting to convert TTL to integer, possibly returning a float.
- Not handling the case when TTL is not set (infinity) – TTL should return -1.
- Not deleting expired keys, causing stale data to linger.
- Using time.sleep in tests instead of mocking time to avoid slow tests.
Variations
- Use `unittest.mock` to replace actual Redis calls with a MagicMock.
- Use a real Redis server with `redis-py` in test containers for integration tests.
Real-world use cases
- Unit testing caching logic without spinning up a Redis instance in CI pipelines.
- Simulating cache expiration behavior in a local development environment.
- Teaching or demonstrating Redis TTL semantics in an educational setting.
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.