feat: add Redis-backed rate limiter with configurable backend

Add RedisRateLimiter using sorted-set sliding window alongside the
existing InMemoryRateLimiter. Users choose via RATE_LIMIT_BACKEND
(memory|redis) with RATE_LIMIT_REDIS_URL falling back to RQ_REDIS_URL.
Redis backend validates connectivity at startup and fails open on
transient errors during requests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hugh Brown
2026-03-04 02:15:14 -07:00
committed by Abhimanyu Saharan
parent ee825fb2d5
commit fc9fc1661c
5 changed files with 385 additions and 14 deletions

View File

@@ -34,6 +34,8 @@ from app.api.users import router as users_router
from app.core.config import settings
from app.core.error_handling import install_error_handling
from app.core.logging import configure_logging, get_logger
from app.core.rate_limit import validate_rate_limit_redis
from app.core.rate_limit_backend import RateLimitBackend
from app.core.security_headers import SecurityHeadersMiddleware
from app.db.session import init_db
from app.schemas.health import HealthStatusResponse
@@ -437,6 +439,11 @@ async def lifespan(_: FastAPI) -> AsyncIterator[None]:
settings.db_auto_migrate,
)
await init_db()
if settings.rate_limit_backend == RateLimitBackend.REDIS:
validate_rate_limit_redis(settings.rate_limit_redis_url)
logger.info("app.lifecycle.rate_limit backend=redis")
else:
logger.info("app.lifecycle.rate_limit backend=memory")
logger.info("app.lifecycle.started")
try:
yield