Hone logo
Hone
Problems

Python Redis Integration: A Simple Caching Layer

This challenge focuses on building a basic caching layer in Python using Redis. Caching is a crucial technique for improving application performance by storing frequently accessed data in a fast-access memory store like Redis, reducing the need to fetch data from slower primary data sources.

Problem Description

You are tasked with creating a Python class that acts as a simple cache for data. This class should interact with a Redis server to store and retrieve data. The goal is to simulate a scenario where a web application might cache expensive computation results or database queries to serve them faster.

Key Requirements:

  1. Connect to Redis: The cache class must be able to establish a connection to a Redis server.
  2. Store Data: Implement a method to store key-value pairs in Redis. The key should be a string, and the value can be any serializable Python object (strings, numbers, dictionaries, lists, etc.).
  3. Retrieve Data: Implement a method to retrieve a value from Redis given its key. If the key does not exist, it should return None or a specified default value.
  4. Data Serialization: Since Redis stores strings, you will need to serialize Python objects into a format Redis can store (e.g., JSON) and deserialize them back when retrieved.
  5. TTL (Time To Live): Implement functionality to set an expiration time for cached items. This is essential for preventing stale data from being served.
  6. Cache Invalidation (Optional but Recommended): Consider how you might remove an item from the cache.

Expected Behavior:

  • When data is stored with a TTL, it should become unavailable after the TTL expires.
  • Retrieving a non-existent key should gracefully handle the absence of data.
  • Complex Python objects should be stored and retrieved correctly after serialization/deserialization.

Edge Cases to Consider:

  • What happens if the Redis server is unavailable during connection or operation?
  • How will you handle very large data objects? (Though for this challenge, we'll assume reasonable sizes).
  • What if the value stored is None?

Examples

Example 1: Basic Get/Set

# Assume Redis is running on localhost:6379

cache = RedisCache(host='localhost', port=6379, db=0)

# Store a simple string
cache.set('greeting', 'Hello, Redis!')

# Retrieve the string
retrieved_greeting = cache.get('greeting')
print(f"Retrieved: {retrieved_greeting}")

# Store a dictionary
user_data = {'id': 123, 'name': 'Alice', 'email': 'alice@example.com'}
cache.set('user:123', user_data)

# Retrieve the dictionary
retrieved_user = cache.get('user:123')
print(f"Retrieved User: {retrieved_user}")

Expected Output:

Retrieved: Hello, Redis!
Retrieved User: {'id': 123, 'name': 'Alice', 'email': 'alice@example.com'}

Explanation: The set method serializes the Python objects (string and dictionary) into JSON strings and stores them in Redis. The get method retrieves the JSON strings and deserializes them back into Python objects.

Example 2: With TTL

import time

# Assume Redis is running on localhost:6379

cache = RedisCache(host='localhost', port=6379, db=0)

# Store a value with a 2-second TTL
cache.set('temp_data', 'This will expire', ttl=2)

print("Data set with 2-second TTL.")

# Try to retrieve immediately
print(f"Immediately after set: {cache.get('temp_data')}")

# Wait for more than 2 seconds
time.sleep(3)

# Try to retrieve after expiration
print(f"After expiration: {cache.get('temp_data')}")

Expected Output:

Data set with 2-second TTL.
Immediately after set: This will expire
After expiration: None

Explanation: The set method with ttl=2 instructs Redis to automatically delete the key 'temp_data' after 2 seconds. After the delay, get returns None because the key no longer exists.

Example 3: Retrieving Non-existent Key

# Assume Redis is running on localhost:6379

cache = RedisCache(host='localhost', port=6379, db=0)

# Attempt to retrieve a key that doesn't exist
non_existent_data = cache.get('non_existent_key')
print(f"Retrieving non-existent key: {non_existent_data}")

# Attempt to retrieve a key with a default value
default_value = cache.get('another_missing_key', default='default_data')
print(f"Retrieving with default: {default_value}")

Expected Output:

Retrieving non-existent key: None
Retrieving with default: default_data

Explanation: When a key is not found in Redis, get returns None by default. If a default argument is provided and the key is not found, that default value is returned.

Constraints

  • You must use the redis-py library for interacting with Redis.
  • The Redis server is assumed to be running on localhost with default port 6379 and database 0, unless otherwise specified during instantiation.
  • Serialization should handle standard Python data types: strings, integers, floats, booleans, lists, and dictionaries. JSON is a recommended serialization format.
  • Your cache class should be named RedisCache.
  • The get method should accept an optional default argument.
  • The set method should accept an optional ttl argument (in seconds).

Notes

  • Consider error handling for Redis connection issues. While full-blown exception handling might be complex for this challenge, acknowledge where it would be important.
  • For serialization, Python's built-in json module is a good choice.
  • Think about how you would implement a delete or invalidate method. This is not strictly required for the base challenge but is a good extension to consider.
  • The redis-py library provides methods like set, get, and setex (for setting with expiration) that you can leverage.
  • Make sure to handle the case where redis-py might return bytes for retrieved values, and convert them back to strings or appropriate Python types.
Loading editor...
python