release: bump version to 0.3.0
- Refactor Redis backend connection handling and pool management - Update algorithm implementations with improved type annotations - Enhance config loader validation with stricter Pydantic schemas - Improve decorator and middleware error handling - Expand example scripts with better docstrings and usage patterns - Add new 00_basic_usage.py example for quick start - Reorganize examples directory structure - Fix type annotation inconsistencies across core modules - Update dependencies in pyproject.toml
This commit is contained in:
266
docs/api/backends.rst
Normal file
266
docs/api/backends.rst
Normal file
@@ -0,0 +1,266 @@
|
||||
Backends API
|
||||
============
|
||||
|
||||
Storage backends for rate limit state.
|
||||
|
||||
Backend (Base Class)
|
||||
--------------------
|
||||
|
||||
.. py:class:: Backend
|
||||
|
||||
Abstract base class for rate limit storage backends.
|
||||
|
||||
All backends must implement these methods:
|
||||
|
||||
.. py:method:: get(key)
|
||||
:async:
|
||||
|
||||
Get the current state for a key.
|
||||
|
||||
:param key: The rate limit key.
|
||||
:type key: str
|
||||
:returns: The stored state dictionary or None if not found.
|
||||
:rtype: dict[str, Any] | None
|
||||
|
||||
.. py:method:: set(key, value, *, ttl)
|
||||
:async:
|
||||
|
||||
Set the state for a key with TTL.
|
||||
|
||||
:param key: The rate limit key.
|
||||
:type key: str
|
||||
:param value: The state dictionary to store.
|
||||
:type value: dict[str, Any]
|
||||
:param ttl: Time-to-live in seconds.
|
||||
:type ttl: float
|
||||
|
||||
.. py:method:: delete(key)
|
||||
:async:
|
||||
|
||||
Delete the state for a key.
|
||||
|
||||
:param key: The rate limit key.
|
||||
:type key: str
|
||||
|
||||
.. py:method:: exists(key)
|
||||
:async:
|
||||
|
||||
Check if a key exists.
|
||||
|
||||
:param key: The rate limit key.
|
||||
:type key: str
|
||||
:returns: True if the key exists.
|
||||
:rtype: bool
|
||||
|
||||
.. py:method:: increment(key, amount=1)
|
||||
:async:
|
||||
|
||||
Atomically increment a counter.
|
||||
|
||||
:param key: The rate limit key.
|
||||
:type key: str
|
||||
:param amount: The amount to increment by.
|
||||
:type amount: int
|
||||
:returns: The new value after incrementing.
|
||||
:rtype: int
|
||||
|
||||
.. py:method:: clear()
|
||||
:async:
|
||||
|
||||
Clear all rate limit data.
|
||||
|
||||
.. py:method:: close()
|
||||
:async:
|
||||
|
||||
Close the backend connection.
|
||||
|
||||
Backends support async context manager protocol:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async with MemoryBackend() as backend:
|
||||
await backend.set("key", {"count": 1}, ttl=60)
|
||||
|
||||
MemoryBackend
|
||||
-------------
|
||||
|
||||
.. py:class:: MemoryBackend(max_size=10000, cleanup_interval=60)
|
||||
|
||||
In-memory storage backend with LRU eviction and TTL cleanup.
|
||||
|
||||
:param max_size: Maximum number of keys to store.
|
||||
:type max_size: int
|
||||
:param cleanup_interval: How often to clean expired entries (seconds).
|
||||
:type cleanup_interval: float
|
||||
|
||||
**Usage:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from fastapi_traffic import MemoryBackend, RateLimiter
|
||||
|
||||
backend = MemoryBackend(max_size=10000)
|
||||
limiter = RateLimiter(backend)
|
||||
|
||||
.. py:method:: get_stats()
|
||||
|
||||
Get statistics about the backend.
|
||||
|
||||
:returns: Dictionary with stats like key count, memory usage.
|
||||
:rtype: dict[str, Any]
|
||||
|
||||
.. py:method:: start_cleanup()
|
||||
:async:
|
||||
|
||||
Start the background cleanup task.
|
||||
|
||||
.. py:method:: stop_cleanup()
|
||||
:async:
|
||||
|
||||
Stop the background cleanup task.
|
||||
|
||||
SQLiteBackend
|
||||
-------------
|
||||
|
||||
.. py:class:: SQLiteBackend(db_path, cleanup_interval=300)
|
||||
|
||||
SQLite storage backend for persistent rate limiting.
|
||||
|
||||
:param db_path: Path to the SQLite database file.
|
||||
:type db_path: str | Path
|
||||
:param cleanup_interval: How often to clean expired entries (seconds).
|
||||
:type cleanup_interval: float
|
||||
|
||||
**Usage:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from fastapi_traffic import SQLiteBackend, RateLimiter
|
||||
|
||||
backend = SQLiteBackend("rate_limits.db")
|
||||
limiter = RateLimiter(backend)
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
await limiter.initialize()
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown():
|
||||
await limiter.close()
|
||||
|
||||
.. py:method:: initialize()
|
||||
:async:
|
||||
|
||||
Initialize the database schema.
|
||||
|
||||
Features:
|
||||
|
||||
- WAL mode for better concurrent performance
|
||||
- Automatic schema creation
|
||||
- Connection pooling
|
||||
- Background cleanup of expired entries
|
||||
|
||||
RedisBackend
|
||||
------------
|
||||
|
||||
.. py:class:: RedisBackend
|
||||
|
||||
Redis storage backend for distributed rate limiting.
|
||||
|
||||
.. py:method:: from_url(url, *, key_prefix="", **kwargs)
|
||||
:classmethod:
|
||||
|
||||
Create a RedisBackend from a Redis URL. This is an async classmethod.
|
||||
|
||||
:param url: Redis connection URL.
|
||||
:type url: str
|
||||
:param key_prefix: Prefix for all keys.
|
||||
:type key_prefix: str
|
||||
:returns: Configured RedisBackend instance.
|
||||
:rtype: RedisBackend
|
||||
|
||||
**Usage:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from fastapi_traffic.backends.redis import RedisBackend
|
||||
from fastapi_traffic import RateLimiter
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
backend = await RedisBackend.from_url("redis://localhost:6379/0")
|
||||
limiter = RateLimiter(backend)
|
||||
|
||||
**Connection examples:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Simple connection
|
||||
backend = await RedisBackend.from_url("redis://localhost:6379/0")
|
||||
|
||||
# With password
|
||||
backend = await RedisBackend.from_url("redis://:password@localhost:6379/0")
|
||||
|
||||
# With key prefix
|
||||
backend = await RedisBackend.from_url(
|
||||
"redis://localhost:6379/0",
|
||||
key_prefix="myapp:ratelimit:",
|
||||
)
|
||||
|
||||
.. py:method:: get_stats()
|
||||
:async:
|
||||
|
||||
Get statistics about the Redis backend.
|
||||
|
||||
:returns: Dictionary with stats like key count, memory usage.
|
||||
:rtype: dict[str, Any]
|
||||
|
||||
Features:
|
||||
|
||||
- Atomic operations via Lua scripts
|
||||
- Automatic key expiration
|
||||
- Connection pooling
|
||||
- Support for Redis Sentinel and Cluster
|
||||
|
||||
Implementing Custom Backends
|
||||
----------------------------
|
||||
|
||||
To create a custom backend, inherit from ``Backend`` and implement all abstract
|
||||
methods:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from fastapi_traffic.backends.base import Backend
|
||||
from typing import Any
|
||||
|
||||
class MyBackend(Backend):
|
||||
async def get(self, key: str) -> dict[str, Any] | None:
|
||||
# Retrieve state from your storage
|
||||
pass
|
||||
|
||||
async def set(self, key: str, value: dict[str, Any], *, ttl: float) -> None:
|
||||
# Store state with expiration
|
||||
pass
|
||||
|
||||
async def delete(self, key: str) -> None:
|
||||
# Remove a key
|
||||
pass
|
||||
|
||||
async def exists(self, key: str) -> bool:
|
||||
# Check if key exists
|
||||
pass
|
||||
|
||||
async def increment(self, key: str, amount: int = 1) -> int:
|
||||
# Atomically increment (important for accuracy)
|
||||
pass
|
||||
|
||||
async def clear(self) -> None:
|
||||
# Clear all data
|
||||
pass
|
||||
|
||||
async def close(self) -> None:
|
||||
# Clean up connections
|
||||
pass
|
||||
|
||||
The ``value`` dictionary contains algorithm-specific state. Your backend should
|
||||
serialize it appropriately (JSON works well for most cases).
|
||||
Reference in New Issue
Block a user