Ctrl K
ComunidadGuías…

app/rate_limiter.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
from datetime import datetime, UTC
from typing import Optional

import redis.exceptions
import werkzeug.exceptions
from limits.storage import RedisStorage

from app import metrics
from app.log import LOG

lock_redis: Optional[RedisStorage] = None

rateLimitsEnabled: bool = True


def set_redis_concurrent_lock(redis: RedisStorage):
    global lock_redis
    lock_redis = redis


def set_rate_limit_enabled(enabled: bool):
    global rateLimitsEnabled
    rateLimitsEnabled = enabled


def check_bucket_limit(
    lock_name: Optional[str] = None,
    max_hits: int = 5,
    bucket_seconds: int = 3600,
):
    if not rateLimitsEnabled:
        return
    # Calculate current bucket time
    int_time = int(datetime.now(UTC).timestamp())
    bucket_id = int_time - (int_time % bucket_seconds)
    bucket_lock_name = f"bl:{lock_name}:{bucket_id}"
    if not lock_redis:
        return
    try:
        value = lock_redis.incr(bucket_lock_name, bucket_seconds)
        if value > max_hits:
            LOG.i(
                f"Rate limit hit for {lock_name} (bucket id {bucket_id}) -> {value}/{max_hits}"
            )
            metrics.record_event(
                "BucketRateLimit",
                # Keys look like "alias_create_7:<user id>": keep the limit name
                # only, a per user value would create one metric series per user.
                {
                    "lock_name": (lock_name or "").split(":", 1)[0],
                    "bucket_seconds": bucket_seconds,
                },
            )
            raise werkzeug.exceptions.TooManyRequests()
    except (redis.exceptions.RedisError, AttributeError):
        LOG.e("Cannot connect to redis")