Skip to content

Commit

Permalink
CV2-4719 initial idea saround caching layer in presto
Browse files Browse the repository at this point in the history
  • Loading branch information
DGaffney committed Jun 17, 2024
1 parent af00d9e commit 4caab12
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
51 changes: 51 additions & 0 deletions lib/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import redis
import json
from typing import Any, Optional
from lib.helpers import get_environment_setting

REDIS_URL = get_environment_setting("REDIS_URL")
DEFAULT_TTL = int(get_environment_setting("CACHE_DEFAULT_TTL") or 24*60*60)
class Cache:
@staticmethod
def get_client() -> redis.Redis:
"""
Get a Redis client instance using the provided REDIS_URL.
Returns:
redis.Redis: Redis client instance.
"""
return redis.Redis.from_url(REDIS_URL)

@staticmethod
def get_cached_result(content_hash: str, reset_ttl: bool = True, ttl: int = DEFAULT_TTL) -> Optional[Any]:
"""
Retrieve the cached result for the given content hash. By default, reset the TTL to 24 hours.
Args:
content_hash (str): The key for the cached content.
reset_ttl (bool): Whether to reset the TTL upon access. Default is True.
ttl (int): Time-to-live for the cache in seconds. Default is 86400 seconds (24 hours).
Returns:
Optional[Any]: The cached result, or None if the key does not exist.
"""
client = Cache.get_client()
cached_result = client.get(content_hash)
if cached_result is not None:
if reset_ttl:
client.expire(content_hash, ttl)
return json.loads(cached_result)
return None

@staticmethod
def set_cached_result(content_hash: str, result: Any, ttl: int = DEFAULT_TTL) -> None:
"""
Store the result in the cache with the given content hash and TTL.
Args:
content_hash (str): The key for the cached content.
result (Any): The result to cache.
ttl (int): Time-to-live for the cache in seconds. Default is 86400 seconds (24 hours).
"""
client = Cache.get_client()
client.setex(content_hash, ttl, json.dumps(result))
7 changes: 6 additions & 1 deletion lib/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ def respond(self, messages: Union[List[schemas.Message], schemas.Message]) -> Li
if not isinstance(messages, list):
messages = [messages]
for message in messages:
message.body.result = self.process(message)
existing = Cache.get_cached_result(message.body.content_hash)
if existing:
message.body.result = existing
else:
message.body.result = self.process(message)
Cache.set_cached_result(message.body.content_hash, message.body.result)
return messages

@classmethod
Expand Down
1 change: 1 addition & 0 deletions lib/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class YakeKeywordsResponse(BaseModel):

class GenericItem(BaseModel):
id: Union[str, int, float]
content_hash: Optional[str] = None
callback_url: Optional[str] = None
url: Optional[str] = None
text: Optional[str] = None
Expand Down
52 changes: 52 additions & 0 deletions test/lib/test_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pytest
from unittest.mock import patch, MagicMock
from lib.cache import Cache

# Mock the Redis client and its methods
@pytest.fixture
def mock_redis_client():
with patch('lib.cache.redis.Redis') as mock_redis:
yield mock_redis

def test_set_cached_result(mock_redis_client):
mock_instance = mock_redis_client.from_url.return_value
content_hash = "test_hash"
result = {"data": "example"}
ttl = 3600

Cache.set_cached_result(content_hash, result, ttl)

mock_instance.setex.assert_called_once_with(content_hash, ttl, '{"data": "example"}')

def test_get_cached_result_exists(mock_redis_client):
mock_instance = mock_redis_client.from_url.return_value
content_hash = "test_hash"
ttl = 3600
cached_data = '{"data": "example"}'
mock_instance.get.return_value = cached_data

result = Cache.get_cached_result(content_hash, reset_ttl=True, ttl=ttl)

assert result == {"data": "example"}
mock_instance.expire.assert_called_once_with(content_hash, ttl)

def test_get_cached_result_not_exists(mock_redis_client):
mock_instance = mock_redis_client.from_url.return_value
content_hash = "test_hash"
mock_instance.get.return_value = None

result = Cache.get_cached_result(content_hash)

assert result is None
mock_instance.expire.assert_not_called()

def test_get_cached_result_no_ttl_reset(mock_redis_client):
mock_instance = mock_redis_client.from_url.return_value
content_hash = "test_hash"
cached_data = '{"data": "example"}'
mock_instance.get.return_value = cached_data

result = Cache.get_cached_result(content_hash, reset_ttl=False)

assert result == {"data": "example"}
mock_instance.expire.assert_not_called()

0 comments on commit 4caab12

Please sign in to comment.