teoat commited on
Commit
b5a8a21
·
verified ·
1 Parent(s): 2a6ebf8

Upload core/cache/__init__.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. core/cache/__init__.py +169 -0
core/cache/__init__.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Redis caching service for database queries"""
2
+
3
+ import functools
4
+ import hashlib
5
+ import json
6
+ import logging
7
+ from collections.abc import Callable
8
+ from typing import Any, TypeVar
9
+
10
+ try:
11
+ import redis
12
+
13
+ REDIS_AVAILABLE = True
14
+ except ImportError:
15
+ REDIS_AVAILABLE = False
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ class CacheService:
23
+ def __init__(self, host: str = "localhost", port: int = 6379, db: int = 0):
24
+ if REDIS_AVAILABLE:
25
+ try:
26
+ self.redis_client = redis.Redis(
27
+ host=host,
28
+ port=port,
29
+ db=db,
30
+ decode_responses=True,
31
+ socket_connect_timeout=1,
32
+ )
33
+ self.redis_client.ping()
34
+ self.enabled = True
35
+ except (redis.ConnectionError, Exception) as e:
36
+ logger.warning(f"Redis not available: {e}. Caching disabled.")
37
+ self.redis_client = None
38
+ self.enabled = False
39
+ else:
40
+ self.redis_client = None
41
+ self.enabled = False
42
+ logger.warning("Redis library not installed, caching disabled")
43
+
44
+ def _generate_key(self, prefix: str, *args, **kwargs) -> str:
45
+ """Generate cache key from arguments"""
46
+ # Create a stable string representation of args and kwargs
47
+ key_parts = [prefix]
48
+ if args:
49
+ key_parts.append(str(args))
50
+ if kwargs:
51
+ key_parts.append(json.dumps(kwargs, sort_keys=True, default=str))
52
+
53
+ key_data = ":".join(key_parts)
54
+ return hashlib.md5(key_data.encode()).hexdigest()
55
+
56
+ def get(self, key: str) -> Any | None:
57
+ """Get value from cache"""
58
+ if not self.enabled:
59
+ return None
60
+
61
+ try:
62
+ value = self.redis_client.get(key)
63
+ if value:
64
+ return json.loads(value)
65
+ except Exception as e:
66
+ logger.warning(f"Cache get error: {e}")
67
+ return None
68
+
69
+ def set(self, key: str, value: Any, ttl: int = 300):
70
+ """Set value in cache with TTL (default 5 minutes)"""
71
+ if not self.enabled:
72
+ return
73
+
74
+ try:
75
+ self.redis_client.setex(key, ttl, json.dumps(value, default=str))
76
+ except Exception as e:
77
+ logger.warning(f"Cache set error: {e}")
78
+
79
+ def delete(self, key: str):
80
+ """Delete key from cache"""
81
+ if not self.enabled:
82
+ return
83
+
84
+ try:
85
+ self.redis_client.delete(key)
86
+ except Exception as e:
87
+ logger.warning(f"Cache delete error: {e}")
88
+
89
+ def invalidate_pattern(self, pattern: str):
90
+ """Invalidate all keys matching pattern"""
91
+ if not self.enabled:
92
+ return
93
+
94
+ try:
95
+ keys = list(self.redis_client.scan_iter(match=pattern))
96
+ if keys:
97
+ self.redis_client.delete(*keys)
98
+ except Exception as e:
99
+ logger.warning(f"Cache invalidation error: {e}")
100
+
101
+
102
+ # Global cache instance
103
+ cache_service = CacheService()
104
+
105
+
106
+ def redis_cache(ttl: int = 300, prefix: str | None = None):
107
+ """
108
+ Decorator for caching function results in Redis
109
+
110
+ Args:
111
+ ttl: Time to live in seconds (default 300)
112
+ prefix: Optional prefix for cache key (default: function name)
113
+ """
114
+
115
+ def decorator(func: Callable[..., T]) -> Callable[..., T]:
116
+ @functools.wraps(func)
117
+ def wrapper(*args, **kwargs) -> T:
118
+ if not cache_service.enabled:
119
+ return func(*args, **kwargs)
120
+
121
+ # Generate cache key
122
+ key_prefix = prefix or func.__name__
123
+ cache_key = cache_service._generate_key(key_prefix, *args, **kwargs)
124
+
125
+ # Try to get from cache
126
+ cached_value = cache_service.get(cache_key)
127
+ if cached_value is not None:
128
+ return cached_value
129
+
130
+ # Execute function
131
+ result = func(*args, **kwargs)
132
+
133
+ # Cache result
134
+ if result is not None:
135
+ cache_service.set(cache_key, result, ttl)
136
+
137
+ return result
138
+
139
+ @functools.wraps(func)
140
+ async def async_wrapper(*args, **kwargs) -> T:
141
+ if not cache_service.enabled:
142
+ return await func(*args, **kwargs)
143
+
144
+ # Generate cache key
145
+ key_prefix = prefix or func.__name__
146
+ cache_key = cache_service._generate_key(key_prefix, *args, **kwargs)
147
+
148
+ # Try to get from cache
149
+ cached_value = cache_service.get(cache_key)
150
+ if cached_value is not None:
151
+ return cached_value
152
+
153
+ # Execute function
154
+ result = await func(*args, **kwargs)
155
+
156
+ # Cache result
157
+ if result is not None:
158
+ cache_service.set(cache_key, result, ttl)
159
+
160
+ return result
161
+
162
+ # Return appropriate wrapper based on sync/async
163
+ import asyncio
164
+
165
+ if asyncio.iscoroutinefunction(func):
166
+ return async_wrapper
167
+ return wrapper
168
+
169
+ return decorator