0
# Sharded Caching
1
2
FanoutCache provides high-throughput caching by automatically distributing keys across multiple Cache instances (shards). This sharding approach improves performance for applications with high concurrency by reducing lock contention and enabling parallel operations across different shards.
3
4
## Capabilities
5
6
### FanoutCache Initialization
7
8
Create a FanoutCache instance with configurable shard count and settings.
9
10
```python { .api }
11
class FanoutCache:
12
def __init__(self, directory=None, shards=8, timeout=0.010, disk=Disk, **settings):
13
"""
14
Initialize sharded cache instance.
15
16
Args:
17
directory (str, optional): Cache directory path. If None, creates temp directory.
18
shards (int): Number of shards to distribute writes across. Default 8.
19
timeout (float): SQLite connection timeout in seconds. Default 0.010.
20
disk (Disk): Disk instance for serialization. Default Disk.
21
**settings: Cache configuration options from DEFAULT_SETTINGS.
22
size_limit is automatically divided by shard count.
23
"""
24
25
@property
26
def directory(self):
27
"""Cache directory path."""
28
```
29
30
### Cache Operations
31
32
All standard cache operations with automatic key distribution across shards.
33
34
```python { .api }
35
def set(self, key, value, expire=None, read=False, tag=None, retry=False):
36
"""
37
Store key-value pair in appropriate shard.
38
39
Args:
40
key: Cache key (must be hashable)
41
value: Value to store
42
expire (float, optional): Expiration time in seconds from now
43
read (bool): Store value as file for reading. Default False.
44
tag (str, optional): Tag for grouping related items
45
retry (bool): Retry operation on timeout. Default False.
46
47
Returns:
48
bool: True if set succeeded
49
"""
50
51
def get(self, key, default=None, read=False, expire_time=False, tag=False, retry=False):
52
"""
53
Retrieve value by key from appropriate shard.
54
55
Args:
56
key: Cache key
57
default: Default value if key not found
58
read (bool): Return file handle instead of value. Default False.
59
expire_time (bool): Include expiration time in result. Default False.
60
tag (bool): Include tag in result. Default False.
61
retry (bool): Retry operation on timeout. Default False.
62
63
Returns:
64
Value, or tuple with additional info if expire_time/tag requested
65
"""
66
67
def delete(self, key, retry=False):
68
"""
69
Delete key from appropriate shard.
70
71
Args:
72
key: Cache key to delete
73
retry (bool): Retry operation on timeout. Default False.
74
75
Returns:
76
bool: True if key existed and was deleted
77
"""
78
79
def add(self, key, value, expire=None, read=False, tag=None, retry=False):
80
"""
81
Add key-value pair only if key doesn't exist in any shard.
82
83
Args:
84
key: Cache key
85
value: Value to store
86
expire (float, optional): Expiration time in seconds from now
87
read (bool): Store value as file for reading. Default False.
88
tag (str, optional): Tag for grouping related items
89
retry (bool): Retry operation on timeout. Default False.
90
91
Returns:
92
bool: True if key was added (didn't exist)
93
"""
94
95
def touch(self, key, expire=None, retry=False):
96
"""
97
Update expiration time for existing key in appropriate shard.
98
99
Args:
100
key: Cache key
101
expire (float, optional): New expiration time in seconds from now
102
retry (bool): Retry operation on timeout. Default False.
103
104
Returns:
105
bool: True if key existed and was touched
106
"""
107
108
def pop(self, key, default=None, expire_time=False, tag=False, retry=False):
109
"""
110
Remove and return value for key from appropriate shard.
111
112
Args:
113
key: Cache key
114
default: Default value if key not found
115
expire_time (bool): Include expiration time in result. Default False.
116
tag (bool): Include tag in result. Default False.
117
retry (bool): Retry operation on timeout. Default False.
118
119
Returns:
120
Value, or tuple with additional info if expire_time/tag requested
121
"""
122
123
def read(self, key):
124
"""
125
Get file handle for key stored in read mode from appropriate shard.
126
127
Args:
128
key: Cache key
129
130
Returns:
131
File handle or None if key not found
132
"""
133
134
def incr(self, key, delta=1, default=0, retry=False):
135
"""
136
Atomically increment numeric value in appropriate shard.
137
138
Args:
139
key: Cache key
140
delta (int): Amount to increment. Default 1.
141
default (int): Default value if key doesn't exist. Default 0.
142
retry (bool): Retry operation on timeout. Default False.
143
144
Returns:
145
New value after increment
146
"""
147
148
def decr(self, key, delta=1, default=0, retry=False):
149
"""
150
Atomically decrement numeric value in appropriate shard.
151
152
Args:
153
key: Cache key
154
delta (int): Amount to decrement. Default 1.
155
default (int): Default value if key doesn't exist. Default 0.
156
retry (bool): Retry operation on timeout. Default False.
157
158
Returns:
159
New value after decrement
160
"""
161
```
162
163
### Dict-like Interface
164
165
Familiar dictionary operations with sharding handled transparently.
166
167
```python { .api }
168
def __setitem__(self, key, value):
169
"""Store key-value pair using fanout_cache[key] = value syntax."""
170
171
def __getitem__(self, key):
172
"""Retrieve value using fanout_cache[key] syntax. Raises KeyError if not found."""
173
174
def __delitem__(self, key):
175
"""Delete key using del fanout_cache[key] syntax."""
176
177
def __contains__(self, key):
178
"""Check if key exists using 'key in fanout_cache' syntax."""
179
180
def __len__(self):
181
"""Get total count of items across all shards."""
182
183
def __iter__(self):
184
"""Iterate over all cache keys across all shards."""
185
186
def __reversed__(self):
187
"""Reverse iterate over all cache keys across all shards."""
188
```
189
190
### Cache Management
191
192
Management operations that work across all shards.
193
194
```python { .api }
195
def clear(self, retry=False):
196
"""
197
Remove all items from all shards.
198
199
Args:
200
retry (bool): Retry operation on timeout. Default False.
201
202
Returns:
203
int: Total number of items removed across all shards
204
"""
205
206
def cull(self, retry=False):
207
"""
208
Remove items according to eviction policy from all shards.
209
210
Args:
211
retry (bool): Retry operation on timeout. Default False.
212
213
Returns:
214
int: Total number of items removed across all shards
215
"""
216
217
def expire(self, retry=False):
218
"""
219
Remove expired items from all shards.
220
221
Args:
222
retry (bool): Retry operation on timeout. Default False.
223
224
Returns:
225
int: Total number of expired items removed across all shards
226
"""
227
228
def evict(self, tag, retry=False):
229
"""
230
Remove all items with specified tag from all shards.
231
232
Args:
233
tag (str): Tag to evict
234
retry (bool): Retry operation on timeout. Default False.
235
236
Returns:
237
int: Total number of items evicted across all shards
238
"""
239
240
def check(self, fix=False, retry=False):
241
"""
242
Check database consistency across all shards.
243
244
Args:
245
fix (bool): Attempt to fix issues if found. Default False.
246
retry (bool): Retry operation on timeout. Default False.
247
248
Returns:
249
List of issues found across all shards
250
"""
251
252
def create_tag_index(self):
253
"""Create database index on tag column for all shards."""
254
255
def drop_tag_index(self):
256
"""Drop database index on tag column for all shards."""
257
```
258
259
### Statistics and Monitoring
260
261
Access aggregated statistics and information across all shards.
262
263
```python { .api }
264
def stats(self, enable=True, reset=False):
265
"""
266
Get aggregated cache hit/miss statistics across all shards.
267
268
Args:
269
enable (bool): Enable statistics tracking on all shards. Default True.
270
reset (bool): Reset statistics counters on all shards. Default False.
271
272
Returns:
273
Tuple of (total_hits, total_misses) across all shards
274
"""
275
276
def volume(self):
277
"""
278
Get total cache size on disk across all shards.
279
280
Returns:
281
int: Total size in bytes across all shards
282
"""
283
```
284
285
### Transaction Management
286
287
Context management and connection handling across shards.
288
289
```python { .api }
290
def transact(self, retry=True):
291
"""
292
Context manager for atomic transactions across relevant shards.
293
294
Args:
295
retry (bool): Retry transaction on timeout. Default True.
296
297
Returns:
298
Context manager for transaction
299
"""
300
301
def __enter__(self):
302
"""Context manager entry - prepare for operations."""
303
304
def __exit__(self, *exception):
305
"""Context manager exit - cleanup resources."""
306
307
def close(self):
308
"""Close database connections and cleanup resources for all shards."""
309
```
310
311
### Sub-collection Access
312
313
Create sub-collections (Cache, Deque, Index) within the FanoutCache directory structure.
314
315
```python { .api }
316
def cache(self, name, timeout=60, disk=None, **settings):
317
"""
318
Return Cache instance in subdirectory.
319
320
Args:
321
name (str): Subdirectory name for Cache
322
timeout (float): SQLite connection timeout. Default 60.
323
disk (Disk, optional): Disk instance. Default uses FanoutCache's disk.
324
**settings: Cache configuration options
325
326
Returns:
327
Cache: Cache instance in subdirectory
328
"""
329
330
def deque(self, name, maxlen=None):
331
"""
332
Return Deque instance in subdirectory.
333
334
Args:
335
name (str): Subdirectory name for Deque
336
maxlen (int, optional): Maximum length of deque
337
338
Returns:
339
Deque: Deque instance in subdirectory
340
"""
341
342
def index(self, name):
343
"""
344
Return Index instance in subdirectory.
345
346
Args:
347
name (str): Subdirectory name for Index
348
349
Returns:
350
Index: Index instance in subdirectory
351
"""
352
```
353
354
### Advanced Operations
355
356
Settings management and serialization support for FanoutCache.
357
358
```python { .api }
359
def reset(self, key, value=ENOVAL):
360
"""
361
Reset cache setting value across all shards.
362
363
Args:
364
key (str): Setting key from DEFAULT_SETTINGS
365
value: New value for setting
366
367
Returns:
368
Previous value of setting from first shard
369
"""
370
371
def __getstate__(self):
372
"""Support for pickle serialization - returns FanoutCache state."""
373
374
def __setstate__(self, state):
375
"""Support for pickle deserialization - restores FanoutCache state."""
376
```
377
378
### Dynamic Settings Access
379
380
Access shard settings dynamically through attribute access.
381
382
```python { .api }
383
def __getattr__(self, name):
384
"""
385
Get setting value from first shard dynamically.
386
387
Args:
388
name (str): Setting name
389
390
Returns:
391
Setting value from first shard
392
393
Raises:
394
AttributeError: If setting name is not found
395
"""
396
```
397
398
## Usage Examples
399
400
### Basic Sharded Caching
401
402
```python
403
import diskcache
404
405
# Create FanoutCache with 16 shards for higher throughput
406
fanout = diskcache.FanoutCache('/tmp/fanout_cache', shards=16)
407
408
# Basic operations - sharding is transparent
409
fanout.set('user:123', {'name': 'Bob', 'role': 'admin'})
410
user = fanout.get('user:123')
411
412
# Dict-like interface
413
fanout['config'] = {'debug': False, 'max_connections': 100}
414
config = fanout['config']
415
416
# Keys are automatically distributed across shards
417
for i in range(1000):
418
fanout.set(f'item:{i}', f'value_{i}')
419
420
print(f"Total items: {len(fanout)}") # Aggregated across all shards
421
```
422
423
### High-Concurrency Usage
424
425
```python
426
import threading
427
import diskcache
428
429
fanout = diskcache.FanoutCache('/tmp/high_throughput', shards=32)
430
431
def worker(thread_id):
432
# Each thread can work with different keys simultaneously
433
# without blocking due to sharding
434
for i in range(1000):
435
key = f'thread_{thread_id}_item_{i}'
436
fanout.set(key, {'thread': thread_id, 'value': i})
437
438
# Create multiple threads for concurrent access
439
threads = []
440
for i in range(10):
441
t = threading.Thread(target=worker, args=(i,))
442
threads.append(t)
443
t.start()
444
445
for t in threads:
446
t.join()
447
448
print(f"Total items: {len(fanout)}")
449
```
450
451
### Using Sub-collections
452
453
```python
454
fanout = diskcache.FanoutCache('/tmp/collections')
455
456
# Create different data structures in subdirectories
457
user_cache = fanout.cache('users')
458
session_index = fanout.index('sessions')
459
task_queue = fanout.deque('tasks')
460
461
# Use each collection independently
462
user_cache.set('user:123', {'name': 'Alice'})
463
session_index['session_abc'] = {'user_id': 123, 'created': 1609459200}
464
task_queue.append({'task': 'send_email', 'user_id': 123})
465
466
# Collections share the same directory structure but operate independently
467
print(f"Users: {len(user_cache)}")
468
print(f"Sessions: {len(session_index)}")
469
print(f"Tasks: {len(task_queue)}")
470
```
471
472
### Performance Monitoring
473
474
```python
475
# Enable statistics across all shards
476
fanout.stats(enable=True)
477
478
# Perform operations
479
for i in range(10000):
480
fanout.set(f'key_{i}', f'value_{i}')
481
482
for i in range(5000):
483
value = fanout.get(f'key_{i}') # Cache hits
484
485
for i in range(5000, 10000):
486
value = fanout.get(f'missing_{i}') # Cache misses
487
488
# Get aggregated statistics
489
hits, misses = fanout.stats()
490
print(f"Total hits: {hits}, misses: {misses}")
491
print(f"Hit ratio: {hits/(hits+misses):.2%}")
492
print(f"Total size: {fanout.volume()} bytes")
493
```
494
495
### Cache Management
496
497
```python
498
# Expire old items across all shards
499
expired_count = fanout.expire()
500
print(f"Expired {expired_count} items")
501
502
# Evict items by tag across all shards
503
fanout.set('temp1', 'data1', tag='temporary')
504
fanout.set('temp2', 'data2', tag='temporary')
505
evicted = fanout.evict('temporary')
506
print(f"Evicted {evicted} temporary items")
507
508
# Clear everything
509
total_cleared = fanout.clear()
510
print(f"Cleared {total_cleared} total items")
511
```