0
# Cache Management
1
2
Cachier provides comprehensive cache management capabilities through methods attached to decorated functions. These methods allow you to control cache behavior, clear cached data, and pre-populate caches with known values.
3
4
## Capabilities
5
6
### Clear Cache
7
8
Remove all cached entries for a specific function:
9
10
```python { .api }
11
def clear_cache() -> None:
12
"""
13
Clear all cached entries for this function.
14
15
Removes all cached results from the function's cache storage.
16
The next call to the function will trigger a fresh calculation
17
regardless of any previously cached values.
18
"""
19
```
20
21
### Clear Being Calculated
22
23
Reset calculation state markers for concurrent access:
24
25
```python { .api }
26
def clear_being_calculated() -> None:
27
"""
28
Mark all entries in this cache as not being calculated.
29
30
Useful for resetting state after process crashes or when
31
calculation locks become stale. This allows other processes
32
to proceed with calculations that were previously marked
33
as being processed.
34
"""
35
```
36
37
### Cache Directory Path
38
39
Get the file system location where cache data is stored:
40
41
```python { .api }
42
def cache_dpath() -> Optional[str]:
43
"""
44
Return the path to the cache directory, if exists; None if not.
45
46
For file-based backends (pickle), returns the directory path
47
where cache files are stored. For other backends (memory,
48
mongo, redis, sql), returns None as they don't use filesystem
49
storage.
50
51
Returns:
52
str: Path to cache directory for file-based backends
53
None: For non-file backends or if directory doesn't exist
54
"""
55
```
56
57
### Precache Value
58
59
Add known values to the cache without function execution:
60
61
```python { .api }
62
def precache_value(*args, value_to_cache, **kwargs):
63
"""
64
Add an initial value to the cache.
65
66
Allows manual population of cache with known results, useful
67
for bootstrapping caches or providing fallback values.
68
69
Parameters:
70
- *args: Positional arguments that would be passed to function
71
- **kwargs: Keyword arguments that would be passed to function
72
- value_to_cache: The result value to store in cache
73
74
Returns:
75
The cached value (same as value_to_cache parameter)
76
"""
77
```
78
79
## Usage Examples
80
81
### Basic Cache Management
82
83
```python
84
from cachier import cachier
85
from datetime import timedelta
86
87
@cachier(stale_after=timedelta(hours=1))
88
def expensive_calculation(n, precision=2):
89
"""Simulate expensive computation."""
90
result = sum(i**precision for i in range(n))
91
return result
92
93
# Use the function normally
94
result1 = expensive_calculation(1000) # Computed and cached
95
result2 = expensive_calculation(1000) # Retrieved from cache
96
97
# Clear the cache when needed
98
expensive_calculation.clear_cache()
99
result3 = expensive_calculation(1000) # Computed again
100
101
# Check cache storage location
102
cache_path = expensive_calculation.cache_dpath()
103
if cache_path:
104
print(f"Cache files stored in: {cache_path}")
105
106
# Pre-populate cache with known values
107
expensive_calculation.precache_value(100, precision=2, value_to_cache=338350)
108
result4 = expensive_calculation(100, precision=2) # Uses precached value
109
```
110
111
### Concurrent Access Management
112
113
```python
114
import threading
115
from cachier import cachier
116
117
@cachier(backend='pickle', wait_for_calc_timeout=30)
118
def shared_computation(data_id):
119
"""Function that might be called concurrently."""
120
import time
121
time.sleep(5) # Simulate long computation
122
return f"processed_{data_id}"
123
124
def worker_thread(thread_id):
125
try:
126
result = shared_computation("shared_data")
127
print(f"Thread {thread_id}: {result}")
128
except Exception as e:
129
print(f"Thread {thread_id} failed: {e}")
130
131
# Start multiple threads
132
threads = []
133
for i in range(5):
134
t = threading.Thread(target=worker_thread, args=(i,))
135
threads.append(t)
136
t.start()
137
138
# If there's a process crash or hanging calculation
139
# Reset the calculation state
140
shared_computation.clear_being_calculated()
141
142
# Wait for all threads
143
for t in threads:
144
t.join()
145
```
146
147
### Cache Pre-population
148
149
```python
150
from cachier import cachier
151
import json
152
153
@cachier(backend='pickle')
154
def api_lookup(user_id, include_details=False):
155
"""Look up user data from API."""
156
# Simulate API call
157
import requests
158
url = f"https://api.example.com/users/{user_id}"
159
if include_details:
160
url += "?details=true"
161
return requests.get(url).json()
162
163
# Pre-populate cache with known test data
164
test_users = {
165
123: {"name": "Alice", "email": "alice@example.com"},
166
456: {"name": "Bob", "email": "bob@example.com"}
167
}
168
169
for user_id, user_data in test_users.items():
170
api_lookup.precache_value(user_id, value_to_cache=user_data)
171
api_lookup.precache_value(
172
user_id,
173
include_details=True,
174
value_to_cache={**user_data, "details": "full_profile"}
175
)
176
177
# Now these calls use precached data
178
alice = api_lookup(123) # Uses precached data
179
bob_detailed = api_lookup(456, include_details=True) # Uses precached data
180
```
181
182
### Selective Cache Clearing
183
184
```python
185
from cachier import cachier
186
import os
187
188
@cachier(backend='pickle', separate_files=True)
189
def process_file(file_path, options=None):
190
"""Process a file with caching."""
191
with open(file_path, 'r') as f:
192
content = f.read()
193
194
if options and options.get('uppercase'):
195
content = content.upper()
196
197
return {"content": content, "size": len(content)}
198
199
# Process several files
200
result1 = process_file("/path/to/file1.txt")
201
result2 = process_file("/path/to/file2.txt", {"uppercase": True})
202
result3 = process_file("/path/to/file3.txt")
203
204
# Clear all cached results
205
process_file.clear_cache()
206
207
# With separate_files=True, you can also manually remove
208
# specific cache files if needed
209
cache_dir = process_file.cache_dpath()
210
if cache_dir and os.path.exists(cache_dir):
211
cache_files = os.listdir(cache_dir)
212
print(f"Cache files: {cache_files}")
213
```
214
215
### Error Recovery
216
217
```python
218
from cachier import cachier
219
import logging
220
221
logger = logging.getLogger(__name__)
222
223
@cachier(backend='mongo', wait_for_calc_timeout=60)
224
def distributed_task(task_id):
225
"""Task that runs on multiple machines."""
226
# Long-running distributed computation
227
return perform_distributed_analysis(task_id)
228
229
def recover_from_crash():
230
"""Recovery procedure after system crash."""
231
try:
232
# Clear any stale calculation locks
233
distributed_task.clear_being_calculated()
234
logger.info("Cleared stale calculation locks")
235
236
# Optionally clear cache if data might be corrupted
237
# distributed_task.clear_cache()
238
# logger.info("Cleared potentially corrupted cache")
239
240
except Exception as e:
241
logger.error(f"Recovery failed: {e}")
242
243
# Call during application startup after crash
244
recover_from_crash()
245
```
246
247
### Cache Inspection and Maintenance
248
249
```python
250
from cachier import cachier
251
import os
252
import json
253
from datetime import datetime
254
255
@cachier(
256
backend='pickle',
257
separate_files=True,
258
cleanup_stale=True,
259
cleanup_interval=timedelta(hours=1)
260
)
261
def monitored_function(param):
262
"""Function with cache monitoring."""
263
return complex_computation(param)
264
265
def inspect_cache():
266
"""Inspect cache state and perform maintenance."""
267
cache_dir = monitored_function.cache_dpath()
268
269
if not cache_dir or not os.path.exists(cache_dir):
270
print("No cache directory found")
271
return
272
273
cache_files = os.listdir(cache_dir)
274
print(f"Found {len(cache_files)} cache files")
275
276
total_size = 0
277
for filename in cache_files:
278
file_path = os.path.join(cache_dir, filename)
279
if os.path.isfile(file_path):
280
size = os.path.getsize(file_path)
281
total_size += size
282
mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
283
print(f" {filename}: {size} bytes, modified {mtime}")
284
285
print(f"Total cache size: {total_size} bytes")
286
287
# Clear cache if it's too large
288
if total_size > 1024 * 1024 * 100: # 100MB
289
print("Cache size exceeds limit, clearing...")
290
monitored_function.clear_cache()
291
292
# Run periodic maintenance
293
inspect_cache()
294
```
295
296
## Method Availability
297
298
These cache management methods are automatically attached to all functions decorated with `@cachier()`:
299
300
- **Available on all decorated functions**: `clear_cache()`, `clear_being_calculated()`, `precache_value()`
301
- **Available only on file-based backends**: `cache_dpath()` returns a path
302
- **Returns None for non-file backends**: `cache_dpath()` for memory, mongo, redis, sql backends
303
304
The methods provide a consistent interface regardless of the backend used, allowing you to write cache management code that works across different storage systems.