0
# Storage Backends
1
2
Storage backends handle the persistence and distribution of rate limit data across different storage systems. The choice of storage backend determines whether rate limiting is local to a single process or distributed across multiple instances.
3
4
## Capabilities
5
6
### Storage Factory
7
8
Factory function for creating storage instances from URI strings, supporting both synchronous and asynchronous storage backends.
9
10
```python { .api }
11
def storage_from_string(storage_string: str, **options: float | str | bool) -> Storage:
12
"""
13
Create storage instance from URI string.
14
15
Supports various storage schemes including memory, Redis, Memcached,
16
MongoDB, and etcd. Can create both sync and async storage instances
17
based on URI scheme.
18
19
Args:
20
storage_string: URI like "redis://localhost:6379" or "memory://"
21
options: Additional options passed to storage constructor
22
23
Returns:
24
Storage instance matching the URI scheme
25
26
Raises:
27
ConfigurationError: If storage scheme is unknown or unsupported
28
29
Examples:
30
memory = storage_from_string("memory://")
31
redis = storage_from_string("redis://localhost:6379")
32
async_redis = storage_from_string("async+redis://localhost:6379")
33
"""
34
```
35
36
### Base Storage Classes
37
38
Abstract base classes defining the storage interface and common functionality.
39
40
```python { .api }
41
from abc import ABC, abstractmethod
42
from limits.util import LazyDependency
43
44
class Storage(LazyDependency, ABC):
45
"""
46
Base class for all storage backends.
47
48
Provides common interface for storing and retrieving rate limit data.
49
Extends LazyDependency to handle optional dependencies for specific backends.
50
"""
51
52
STORAGE_SCHEME: list[str] | None # Supported URI schemes
53
54
def __init__(self, uri: str | None = None, wrap_exceptions: bool = False, **options):
55
"""
56
Initialize storage backend.
57
58
Args:
59
uri: Connection URI for the storage backend
60
wrap_exceptions: Whether to wrap storage exceptions in StorageError
61
options: Additional backend-specific options
62
"""
63
64
@property
65
@abstractmethod
66
def base_exceptions(self) -> type[Exception] | tuple[type[Exception], ...]:
67
"""Base exception types that this storage backend can raise"""
68
69
@abstractmethod
70
def incr(self, key: str, expiry: int, elastic_expiry: bool = False, amount: int = 1) -> int:
71
"""
72
Increment counter for key.
73
74
Args:
75
key: Storage key for the rate limit
76
expiry: Expiration time in seconds
77
elastic_expiry: Whether to use elastic expiry behavior
78
amount: Amount to increment by
79
80
Returns:
81
New counter value after increment
82
"""
83
84
@abstractmethod
85
def get(self, key: str) -> int:
86
"""
87
Get current counter value for key.
88
89
Args:
90
key: Storage key
91
92
Returns:
93
Current counter value, 0 if key doesn't exist
94
"""
95
96
@abstractmethod
97
def get_expiry(self, key: str) -> float:
98
"""
99
Get expiration time for key.
100
101
Args:
102
key: Storage key
103
104
Returns:
105
Expiration timestamp, or 0 if key doesn't exist
106
"""
107
108
@abstractmethod
109
def check(self) -> bool:
110
"""
111
Health check for storage backend.
112
113
Returns:
114
True if storage is accessible and working
115
"""
116
117
@abstractmethod
118
def reset(self) -> int | None:
119
"""Reset/clear all stored data"""
120
121
@abstractmethod
122
def clear(self, key: str) -> None:
123
"""
124
Clear data for specific key.
125
126
Args:
127
key: Storage key to clear
128
"""
129
130
class MovingWindowSupport(ABC):
131
"""Interface for storage backends supporting moving window strategy"""
132
133
@abstractmethod
134
def acquire_entry(self, key: str, limit: int, expiry: int, amount: int = 1) -> bool:
135
"""
136
Acquire entry in moving window.
137
138
Args:
139
key: Storage key
140
limit: Rate limit amount
141
expiry: Window duration in seconds
142
amount: Number of entries to acquire
143
144
Returns:
145
True if entries were acquired, False if limit exceeded
146
"""
147
148
@abstractmethod
149
def get_moving_window(self, key: str, limit: int, expiry: int) -> tuple[float, int]:
150
"""
151
Get current moving window state.
152
153
Args:
154
key: Storage key
155
limit: Rate limit amount
156
expiry: Window duration in seconds
157
158
Returns:
159
Tuple of (window_start_time, current_count)
160
"""
161
162
class SlidingWindowCounterSupport(ABC):
163
"""Interface for storage backends supporting sliding window counter strategy"""
164
165
@abstractmethod
166
def get_sliding_window(self, key: str, expiry: int) -> tuple[int, float, int, float]:
167
"""
168
Get sliding window counter state.
169
170
Args:
171
key: Storage key
172
expiry: Window duration in seconds
173
174
Returns:
175
Tuple of (previous_count, previous_expires_in, current_count, current_expires_in)
176
"""
177
178
@abstractmethod
179
def acquire_sliding_window_entry(self, key: str, limit: int, expiry: int, amount: int) -> bool:
180
"""
181
Acquire entry using sliding window counter.
182
183
Args:
184
key: Storage key
185
limit: Rate limit amount
186
expiry: Window duration in seconds
187
amount: Number of entries to acquire
188
189
Returns:
190
True if entries were acquired, False if limit exceeded
191
"""
192
```
193
194
### Memory Storage
195
196
In-memory storage backend for single-process applications or testing.
197
198
```python { .api }
199
class MemoryStorage(Storage):
200
"""
201
In-memory storage backend.
202
203
Stores rate limit data in process memory. Not suitable for distributed
204
applications but useful for single-process apps, testing, and development.
205
206
Supports all rate limiting strategies including moving window and
207
sliding window counter.
208
"""
209
210
STORAGE_SCHEME = ["memory"]
211
212
def __init__(self):
213
"""Initialize in-memory storage with empty state"""
214
```
215
216
### Redis Storage Backends
217
218
Redis-based storage backends supporting various Redis deployment patterns.
219
220
```python { .api }
221
class RedisStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
222
"""
223
Redis storage backend.
224
225
Uses Redis for distributed rate limiting across multiple application
226
instances. Supports all rate limiting strategies with high performance
227
and reliability.
228
"""
229
230
STORAGE_SCHEME = ["redis", "rediss", "redis+unix"]
231
232
def __init__(self, uri: str, **options):
233
"""
234
Initialize Redis storage.
235
236
Args:
237
uri: Redis connection URI (redis://host:port/db)
238
options: Additional Redis client options
239
"""
240
241
class RedisClusterStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
242
"""
243
Redis Cluster storage backend.
244
245
Supports Redis Cluster deployments for horizontal scaling and
246
high availability scenarios.
247
"""
248
249
STORAGE_SCHEME = ["redis+cluster"]
250
251
def __init__(self, uri: str, **options):
252
"""
253
Initialize Redis Cluster storage.
254
255
Args:
256
uri: Redis cluster URI (redis+cluster://host:port)
257
options: Additional cluster client options
258
"""
259
260
class RedisSentinelStorage(Storage, MovingWindowSupport, SlidingWindowCounterSupport):
261
"""
262
Redis Sentinel storage backend.
263
264
Supports Redis Sentinel for automatic failover and high availability.
265
"""
266
267
STORAGE_SCHEME = ["redis+sentinel"]
268
269
def __init__(self, uri: str, **options):
270
"""
271
Initialize Redis Sentinel storage.
272
273
Args:
274
uri: Sentinel URI (redis+sentinel://host:port/service)
275
options: Additional sentinel options
276
"""
277
```
278
279
### Memcached Storage
280
281
Memcached storage backend for distributed caching scenarios.
282
283
```python { .api }
284
class MemcachedStorage(Storage):
285
"""
286
Memcached storage backend.
287
288
Uses Memcached for distributed rate limiting. Supports basic rate limiting
289
strategies but not moving window or sliding window counter due to
290
Memcached's limited data structure support.
291
"""
292
293
STORAGE_SCHEME = ["memcached"]
294
295
def __init__(self, uri: str, **options):
296
"""
297
Initialize Memcached storage.
298
299
Args:
300
uri: Memcached URI (memcached://host:port)
301
options: Additional memcached client options
302
"""
303
```
304
305
### MongoDB Storage
306
307
MongoDB storage backend for document-based persistence.
308
309
```python { .api }
310
class MongoDBStorageBase(Storage):
311
"""Base class for MongoDB storage implementations"""
312
313
class MongoDBStorage(MongoDBStorageBase, MovingWindowSupport, SlidingWindowCounterSupport):
314
"""
315
MongoDB storage backend.
316
317
Uses MongoDB for persistent rate limiting data with support for all
318
rate limiting strategies. Suitable for applications already using
319
MongoDB or requiring persistent rate limit data.
320
"""
321
322
STORAGE_SCHEME = ["mongodb"]
323
324
def __init__(self, uri: str, **options):
325
"""
326
Initialize MongoDB storage.
327
328
Args:
329
uri: MongoDB connection URI (mongodb://host:port/database)
330
options: Additional MongoDB client options
331
"""
332
```
333
334
### Etcd Storage
335
336
Etcd storage backend for distributed key-value storage.
337
338
```python { .api }
339
class EtcdStorage(Storage):
340
"""
341
Etcd storage backend.
342
343
Uses etcd for distributed rate limiting in Kubernetes and other
344
cloud-native environments. Provides consistency guarantees but
345
with higher latency than Redis.
346
"""
347
348
STORAGE_SCHEME = ["etcd"]
349
350
def __init__(self, uri: str, **options):
351
"""
352
Initialize etcd storage.
353
354
Args:
355
uri: Etcd connection URI (etcd://host:port)
356
options: Additional etcd client options
357
"""
358
```
359
360
### Helper Classes
361
362
Helper classes providing common functionality for storage implementations.
363
364
```python { .api }
365
class TimestampedSlidingWindow:
366
"""Helper class for storage that support sliding window counter with timestamp-based keys"""
367
368
@classmethod
369
def sliding_window_keys(cls, key: str, expiry: int, at: float) -> tuple[str, str]:
370
"""
371
Generate keys for previous and current sliding window buckets.
372
373
Args:
374
key: Base key for the rate limit
375
expiry: Window duration in seconds
376
at: Timestamp to generate keys for (usually current time)
377
378
Returns:
379
Tuple of (previous_window_key, current_window_key)
380
381
Example:
382
With key="mykey", expiry=60, at=1738576292.6631825
383
Returns ("mykey/28976271", "mykey/28976270")
384
"""
385
```
386
387
## Usage Examples
388
389
### Storage Selection
390
391
```python
392
from limits.storage import storage_from_string
393
394
# In-memory storage (single process)
395
memory_storage = storage_from_string("memory://")
396
397
# Redis storage (distributed)
398
redis_storage = storage_from_string("redis://localhost:6379")
399
redis_with_db = storage_from_string("redis://localhost:6379/1")
400
redis_with_auth = storage_from_string("redis://:password@localhost:6379")
401
402
# Valkey storage (Redis-compatible)
403
valkey_storage = storage_from_string("valkey://localhost:6379")
404
valkey_with_ssl = storage_from_string("valkeys://localhost:6380")
405
406
# Redis Cluster
407
cluster_storage = storage_from_string("redis+cluster://localhost:7000")
408
409
# Redis Sentinel
410
sentinel_storage = storage_from_string(
411
"redis+sentinel://localhost:26379/mymaster"
412
)
413
414
# Memcached
415
memcached_storage = storage_from_string("memcached://localhost:11211")
416
417
# MongoDB
418
mongodb_storage = storage_from_string("mongodb://localhost:27017/ratelimits")
419
420
# Etcd
421
etcd_storage = storage_from_string("etcd://localhost:2379")
422
```
423
424
### Storage with Options
425
426
```python
427
from limits.storage import storage_from_string
428
429
# Redis with connection options
430
redis_storage = storage_from_string(
431
"redis://localhost:6379",
432
socket_timeout=5,
433
socket_connect_timeout=5,
434
retry_on_timeout=True,
435
health_check_interval=30
436
)
437
438
# MongoDB with collection name
439
mongodb_storage = storage_from_string(
440
"mongodb://localhost:27017/mydb",
441
collection_name="custom_rate_limits"
442
)
443
444
# Memcached with timeout
445
memcached_storage = storage_from_string(
446
"memcached://localhost:11211",
447
timeout=10
448
)
449
```
450
451
### Testing Storage Connectivity
452
453
```python
454
from limits.storage import storage_from_string
455
from limits.errors import ConfigurationError, StorageError
456
457
try:
458
# Create storage instance
459
storage = storage_from_string("redis://localhost:6379")
460
461
# Test connectivity
462
if storage.check():
463
print("Storage is accessible")
464
else:
465
print("Storage connectivity issues")
466
467
except ConfigurationError as e:
468
print(f"Configuration error: {e}")
469
except StorageError as e:
470
print(f"Storage error: {e}")
471
```
472
473
### Working with Storage Directly
474
475
```python
476
from limits.storage import MemoryStorage
477
478
# Create storage instance
479
storage = MemoryStorage()
480
481
# Basic operations
482
key = "user:12345:api_calls"
483
expiry = 3600 # 1 hour
484
485
# Increment counter
486
current_count = storage.incr(key, expiry, amount=1)
487
print(f"Current count: {current_count}")
488
489
# Get current value
490
count = storage.get(key)
491
print(f"Retrieved count: {count}")
492
493
# Get expiry time
494
expire_time = storage.get_expiry(key)
495
print(f"Expires at: {expire_time}")
496
497
# Clear specific key
498
storage.clear(key)
499
500
# Reset all data
501
storage.reset()
502
```
503
504
### Storage Schemes Reference
505
506
```python { .api }
507
# Available storage schemes
508
SUPPORTED_SCHEMES = {
509
"memory": MemoryStorage,
510
"redis": RedisStorage,
511
"rediss": RedisStorage, # Redis with SSL
512
"redis+unix": RedisStorage, # Redis Unix socket
513
"valkey": RedisStorage, # Valkey backend
514
"valkeys": RedisStorage, # Valkey with SSL
515
"valkey+unix": RedisStorage, # Valkey Unix socket
516
"redis+cluster": RedisClusterStorage,
517
"valkey+cluster": RedisClusterStorage, # Valkey cluster
518
"redis+sentinel": RedisSentinelStorage,
519
"valkey+sentinel": RedisSentinelStorage, # Valkey sentinel
520
"memcached": MemcachedStorage,
521
"mongodb": MongoDBStorage,
522
"mongodb+srv": MongoDBStorage, # MongoDB with SRV records
523
"etcd": EtcdStorage,
524
}
525
526
# Async storage schemes (prefix with "async+")
527
ASYNC_SCHEMES = {
528
"async+memory": "limits.aio.storage.MemoryStorage",
529
"async+redis": "limits.aio.storage.RedisStorage",
530
"async+valkey": "limits.aio.storage.RedisStorage", # Valkey async support
531
"async+memcached": "limits.aio.storage.MemcachedStorage",
532
"async+mongodb": "limits.aio.storage.MongoDBStorage",
533
"async+etcd": "limits.aio.storage.EtcdStorage"
534
}
535
```