2024-01-30 18:29:59 +01:00
|
|
|
from datetime import datetime
|
|
|
|
from typing import Optional
|
|
|
|
|
2024-02-06 11:55:45 +01:00
|
|
|
import newrelic.agent
|
2024-01-30 18:29:59 +01:00
|
|
|
import redis.exceptions
|
|
|
|
import werkzeug.exceptions
|
|
|
|
from limits.storage import RedisStorage
|
|
|
|
|
|
|
|
from app.log import log
|
|
|
|
|
|
|
|
lock_redis: Optional[RedisStorage] = None
|
|
|
|
|
|
|
|
|
|
|
|
def set_redis_concurrent_lock(redis: RedisStorage):
|
|
|
|
global lock_redis
|
|
|
|
lock_redis = redis
|
|
|
|
|
|
|
|
|
|
|
|
def check_bucket_limit(
|
|
|
|
lock_name: Optional[str] = None,
|
|
|
|
max_hits: int = 5,
|
|
|
|
bucket_seconds: int = 3600,
|
|
|
|
):
|
|
|
|
# Calculate current bucket time
|
|
|
|
bucket_id = int(datetime.utcnow().timestamp()) % bucket_seconds
|
|
|
|
bucket_lock_name = f"bl:{lock_name}:{bucket_id}"
|
2024-02-05 14:53:01 +01:00
|
|
|
if not lock_redis:
|
|
|
|
return
|
2024-01-30 18:29:59 +01:00
|
|
|
try:
|
|
|
|
value = lock_redis.incr(bucket_lock_name, bucket_seconds)
|
|
|
|
if value > max_hits:
|
2024-02-06 11:55:45 +01:00
|
|
|
newrelic.agent.record_custom_event(
|
|
|
|
"BucketRateLimit",
|
|
|
|
{"lock_name": lock_name, "bucket_seconds": bucket_seconds},
|
|
|
|
)
|
2024-01-30 18:29:59 +01:00
|
|
|
raise werkzeug.exceptions.TooManyRequests()
|
2024-02-05 14:53:01 +01:00
|
|
|
except (redis.exceptions.RedisError, AttributeError):
|
2024-01-30 18:29:59 +01:00
|
|
|
log.e("Cannot connect to redis")
|