import sqlite3 import json import time import requests from typing import Any, Optional, Dict, List import pickle from concurrent.futures import ThreadPoolExecutor from threading import Lock class CacheManager: def __init__(self, db_name: str = 'pokemon_cache.db', max_connections: int = 10): self.db_name = db_name self.memory_cache: Dict[str, Any] = {} self.expiry_disabled = True self.lock = Lock() self.connection_pool = ThreadPoolExecutor(max_workers=max_connections) self._create_cache_table() self.fetch_url_lock = Lock() def _create_connection(self): return sqlite3.connect(self.db_name) def _create_cache_table(self): with self._create_connection() as conn: cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS cache ( key TEXT PRIMARY KEY, value BLOB, timestamp FLOAT ) ''') conn.commit() def get(self, key: str) -> Optional[Any]: # Check memory cache first with self.lock: if key in self.memory_cache: return self.memory_cache[key] # If not in memory, check database def db_get(): with self._create_connection() as conn: cursor = conn.cursor() cursor.execute('SELECT value, timestamp FROM cache WHERE key = ?', (key,)) result = cursor.fetchone() if result: value, timestamp = result return pickle.loads(value) return None result = self.connection_pool.submit(db_get).result() if result: with self.lock: self.memory_cache[key] = result return result def set(self, key: str, value: Any): serialized_value = pickle.dumps(value) timestamp = time.time() with self.lock: self.memory_cache[key] = value def db_set(): with self._create_connection() as conn: cursor = conn.cursor() cursor.execute(''' INSERT OR REPLACE INTO cache (key, value, timestamp) VALUES (?, ?, ?) ''', (key, serialized_value, timestamp)) conn.commit() self.connection_pool.submit(db_set) def bulk_get(self, keys: List[str]) -> Dict[str, Any]: results = {} db_keys = [] with self.lock: for key in keys: if key in self.memory_cache: results[key] = self.memory_cache[key] else: db_keys.append(key) if db_keys: def db_bulk_get(): with self._create_connection() as conn: cursor = conn.cursor() placeholders = ','.join('?' for _ in db_keys) cursor.execute(f'SELECT key, value FROM cache WHERE key IN ({placeholders})', db_keys) return {key: pickle.loads(value) for key, value in cursor.fetchall()} db_results = self.connection_pool.submit(db_bulk_get).result() with self.lock: self.memory_cache.update(db_results) results.update(db_results) return results def fetch_url(self, url: str, force_refresh: bool = False, expiry: int = 86400) -> Optional[str]: cache_key = f"url_{url}" if not force_refresh: cached_data = self.get(cache_key) if cached_data: cached_time = cached_data['timestamp'] if time.time() - cached_time < expiry or self.expiry_disabled: return cached_data['content'] #with self.fetch_url_lock: print(f"Fetching URL: {url}") response = requests.get(url) if response.status_code == 200: content = response.text self.set(cache_key, { 'content': content, 'timestamp': time.time() }) time.sleep(0.25) return content return None def close(self): self.connection_pool.shutdown() # Usage example if __name__ == "__main__": cache = CacheManager() # Example usage url = "https://example.com" data = cache.fetch_url(url) if data: print("Data fetched successfully") else: print("Failed to fetch data") cache.close()