import pickle from logging import Logger from pymemcache.client.base import Client from app.core.config import settings from app.core.exceptions.base_exception import ( ConnectionException, CouldNotEditMemcache, KeyNotFoundException, ) class Cache: """ A generic cache class for interacting with Memcached. """ def __init__(self, logger: Logger) -> None: """ Initialize the cache connection. :param logger: Logger instance for logging operations """ # Load Memcache config from .env self.host = settings.CACHE_HOST self.port = settings.CACHE_HOST self.default_ttl = settings.CACHE_TTL self.client = self._initialize_connection() self.logger = logger def _initialize_connection(self): """ Establish a connection to the Memcached server. :return: Client instance :raises ConnectionException: If the connection cannot be established """ client = Client((self.host, self.port)) if client: self.logger.info(f"Connected to Memcached at {self.host}: {self.port}") return client else: raise ConnectionException("Could not connect to Memcached server.") def add(self, key: str, value: dict): """ Add an item to the cache. :param key: Cache key :param value: Value to store (serialized using pickle) :raises CouldNotEditMemcache: If the item could not be added """ serialized_value = pickle.dumps(value) res = self.client.add(key, serialized_value, expire=self.default_ttl) if not res: raise CouldNotEditMemcache(f"Could not add key {key} to cache.") self.logger.info(f"Added {key} to cache.") def get(self, key: str): """ Retrieve an item from the cache. :param key: Cache key :return: Deserialized value :raises KeyNotFoundException: If the key is not found in the cache """ byte_string = self.get_raw(key) return pickle.loads(byte_string) def get_raw(self, key: str): """ Retrieve the raw byte string from the cache. :param key: Cache key :return: Raw byte string :raises KeyNotFoundException: If the key is not found in the cache """ byte_string = self.client.get(key) if not byte_string: raise KeyNotFoundException(f"Key {key} not found in cache.") # noqa: E713 return byte_string def delete(self, key: str): """ Delete an item from the cache. :param key: Cache key :return: Result of the delete operation :raises CouldNotEditMemcache: If the item could not be deleted """ res = self.client.delete(key) if not res: raise CouldNotEditMemcache(f"Could not delete key {key} from cache.") self.logger.info(f"Deleted {key} from cache.") return res def update(self, key: str, value: dict): """ Update an item in the cache. :param key: Cache key :param value: New value to store (serialized using pickle) :raises CouldNotEditMemcache: If the item could not be updated """ serialized_value = pickle.dumps(value) res = self.client.set(key, serialized_value, expire=self.default_ttl) if not res: raise CouldNotEditMemcache(f"Could not update key {key} in cache.") self.logger.info(f"Updated {key} in cache.")