more redis cleanup
This commit is contained in:
parent
26d93e36c7
commit
537057ee9a
2 changed files with 1 additions and 182 deletions
|
|
@ -1,165 +0,0 @@
|
|||
defmodule Aprsme.RedisRateLimiter do
|
||||
@moduledoc """
|
||||
Redis-backed distributed rate limiter using sliding window algorithm.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
# 5 seconds timeout for Redis operations
|
||||
@redis_timeout 5000
|
||||
|
||||
def child_spec(_opts) do
|
||||
children = [
|
||||
{Redix, name: __MODULE__, host: redis_host(), port: redis_port(), password: redis_password()}
|
||||
]
|
||||
|
||||
%{
|
||||
id: __MODULE__,
|
||||
type: :supervisor,
|
||||
start: {Supervisor, :start_link, [children, [strategy: :one_for_one, name: :"#{__MODULE__}_supervisor"]]}
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check rate limit using sliding window algorithm.
|
||||
|
||||
## Parameters
|
||||
- bucket: The rate limit bucket (e.g., "user:123" or "ip:192.168.1.1")
|
||||
- limit: Maximum number of requests allowed
|
||||
- window_ms: Time window in milliseconds
|
||||
|
||||
## Returns
|
||||
- {:allow, count} if under limit
|
||||
- {:deny, limit} if over limit
|
||||
"""
|
||||
def check_rate(bucket, limit, window_ms) do
|
||||
now = System.system_time(:millisecond)
|
||||
window_start = now - window_ms
|
||||
key = make_key(bucket)
|
||||
|
||||
# Lua script for atomic sliding window rate limiting
|
||||
lua_script = """
|
||||
local key = KEYS[1]
|
||||
local now = tonumber(ARGV[1])
|
||||
local window_start = tonumber(ARGV[2])
|
||||
local limit = tonumber(ARGV[3])
|
||||
|
||||
-- Remove old entries
|
||||
redis.call('ZREMRANGEBYSCORE', key, 0, window_start)
|
||||
|
||||
-- Count current entries
|
||||
local current = redis.call('ZCARD', key)
|
||||
|
||||
if current < limit then
|
||||
-- Add new entry
|
||||
redis.call('ZADD', key, now, now)
|
||||
-- Set expiry
|
||||
redis.call('EXPIRE', key, math.ceil(ARGV[4] / 1000))
|
||||
return {1, current + 1}
|
||||
else
|
||||
return {0, limit}
|
||||
end
|
||||
"""
|
||||
|
||||
case Redix.command(__MODULE__, ["EVAL", lua_script, 1, key, now, window_start, limit, window_ms],
|
||||
timeout: @redis_timeout
|
||||
) do
|
||||
{:ok, [1, count]} ->
|
||||
{:allow, count}
|
||||
|
||||
{:ok, [0, limit]} ->
|
||||
{:deny, limit}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Redis rate limit error for #{bucket}: #{inspect(reason)}")
|
||||
# Fail open - allow request if Redis is down
|
||||
{:allow, 1}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reset rate limit for a bucket
|
||||
"""
|
||||
def reset(bucket) do
|
||||
key = make_key(bucket)
|
||||
|
||||
case Redix.command(__MODULE__, ["DEL", key], timeout: @redis_timeout) do
|
||||
{:ok, _} ->
|
||||
:ok
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Redis rate limit reset error for #{bucket}: #{inspect(reason)}")
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get current count for a bucket
|
||||
"""
|
||||
def count(bucket, window_ms) do
|
||||
now = System.system_time(:millisecond)
|
||||
window_start = now - window_ms
|
||||
key = make_key(bucket)
|
||||
|
||||
# Clean up old entries and count
|
||||
case Redix.pipeline(
|
||||
__MODULE__,
|
||||
[
|
||||
["ZREMRANGEBYSCORE", key, 0, window_start],
|
||||
["ZCARD", key]
|
||||
],
|
||||
timeout: @redis_timeout
|
||||
) do
|
||||
{:ok, [_, count]} ->
|
||||
{:ok, count}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Redis rate limit count error for #{bucket}: #{inspect(reason)}")
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get remaining requests for a bucket
|
||||
"""
|
||||
def remaining(bucket, limit, window_ms) do
|
||||
case count(bucket, window_ms) do
|
||||
{:ok, current} -> {:ok, max(0, limit - current)}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp make_key(bucket) do
|
||||
"aprsme:rate_limit:#{bucket}"
|
||||
end
|
||||
|
||||
defp redis_host do
|
||||
redis_url = System.get_env("REDIS_URL", "redis://localhost:6379")
|
||||
uri = URI.parse(redis_url)
|
||||
uri.host || "localhost"
|
||||
end
|
||||
|
||||
defp redis_port do
|
||||
redis_url = System.get_env("REDIS_URL", "redis://localhost:6379")
|
||||
uri = URI.parse(redis_url)
|
||||
uri.port || 6379
|
||||
end
|
||||
|
||||
defp redis_password do
|
||||
redis_url = System.get_env("REDIS_URL", "redis://localhost:6379")
|
||||
uri = URI.parse(redis_url)
|
||||
|
||||
case uri.userinfo do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
userinfo ->
|
||||
case String.split(userinfo, ":") do
|
||||
[_, password] -> password
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -69,8 +69,7 @@ defmodule AprsmeWeb.Plugs.HealthCheck do
|
|||
|
||||
defp full_health_checks do
|
||||
# Comprehensive checks for readiness
|
||||
with :ok <- check_database_connection(),
|
||||
:ok <- check_redis_connection() do
|
||||
with :ok <- check_database_connection() do
|
||||
check_pubsub()
|
||||
end
|
||||
end
|
||||
|
|
@ -84,21 +83,6 @@ defmodule AprsmeWeb.Plugs.HealthCheck do
|
|||
_ -> {:error, "Database check failed"}
|
||||
end
|
||||
|
||||
defp check_redis_connection do
|
||||
if System.get_env("REDIS_URL") do
|
||||
try do
|
||||
case Redix.command(:query_cache_redis, ["PING"]) do
|
||||
{:ok, "PONG"} -> :ok
|
||||
_ -> {:error, "Redis connection failed"}
|
||||
end
|
||||
rescue
|
||||
_ -> {:error, "Redis check failed"}
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp check_pubsub do
|
||||
Phoenix.PubSub.broadcast(Aprsme.PubSub, "health_check", :ping)
|
||||
:ok
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue