cleanup and remove redis

This commit is contained in:
Graham McIntire 2025-10-22 16:19:17 -05:00
parent 6acc007360
commit cc4578653f
No known key found for this signature in database
7 changed files with 21 additions and 324 deletions

View file

@ -240,14 +240,7 @@ defmodule Aprsme.Application do
Logger.info("Starting Redis-based caching and rate limiting")
[
# Redis-based rate limiter
Aprsme.RedisRateLimiter,
# Redis-based caches
{Aprsme.RedisCache, name: :query_cache},
{Aprsme.RedisCache, name: :device_cache},
{Aprsme.RedisCache, name: :symbol_cache}
]
[]
else
require Logger

View file

@ -8,70 +8,44 @@ defmodule Aprsme.Cache do
Get a value from cache
"""
def get(cache_name, key) do
if using_redis?() do
Aprsme.RedisCache.get(cache_name, key)
else
Cachex.get(cache_name, key)
end
Cachex.get(cache_name, key)
end
@doc """
Put a value in cache with optional TTL
"""
def put(cache_name, key, value, opts \\ []) do
if using_redis?() do
# Convert TTL from milliseconds to seconds for Redis, preserving sub-second values
opts = convert_ttl_to_seconds(opts)
Aprsme.RedisCache.put(cache_name, key, value, opts)
else
Cachex.put(cache_name, key, value, opts)
end
Cachex.put(cache_name, key, value, opts)
end
@doc """
Delete a key from cache
"""
def del(cache_name, key) do
if using_redis?() do
Aprsme.RedisCache.del(cache_name, key)
else
Cachex.del(cache_name, key)
end
Cachex.del(cache_name, key)
end
@doc """
Clear all keys from cache
"""
def clear(cache_name) do
if using_redis?() do
Aprsme.RedisCache.clear(cache_name)
else
Cachex.clear(cache_name)
end
Cachex.clear(cache_name)
end
@doc """
Get cache statistics
"""
def stats(cache_name) do
if using_redis?() do
Aprsme.RedisCache.stats(cache_name)
else
Cachex.stats(cache_name)
end
Cachex.stats(cache_name)
end
@doc """
Check if key exists
"""
def exists?(cache_name, key) do
if using_redis?() do
Aprsme.RedisCache.exists?(cache_name, key)
else
case Cachex.exists?(cache_name, key) do
{:ok, exists?} -> exists?
{:error, _reason} -> false
end
case Cachex.exists?(cache_name, key) do
{:ok, exists?} -> exists?
{:error, _reason} -> false
end
end
@ -79,35 +53,10 @@ defmodule Aprsme.Cache do
Get TTL for a key
"""
def ttl(cache_name, key) do
if using_redis?() do
Aprsme.RedisCache.ttl(cache_name, key)
else
Cachex.ttl(cache_name, key)
end
Cachex.ttl(cache_name, key)
end
# Helper functions
defp using_redis? do
System.get_env("REDIS_URL") != nil
end
defp convert_ttl_to_seconds(opts) do
case Keyword.get(opts, :ttl) do
nil ->
opts
ttl_ms when is_integer(ttl_ms) and ttl_ms > 0 ->
ttl_seconds = ttl_ms |> Integer.ceil_div(1000) |> max(1)
Keyword.put(opts, :ttl, ttl_seconds)
ttl_ms when is_integer(ttl_ms) ->
Keyword.put(opts, :ttl, ttl_ms)
_ ->
opts
end
end
# Helper functions - no longer needed as we only use Cachex
@doc """
Convert timeout keyword list to milliseconds

View file

@ -1,251 +0,0 @@
defmodule Aprsme.RedisCache do
@moduledoc """
Redis-backed distributed cache implementation.
Provides a similar API to Cachex but uses Redis for distributed caching.
"""
require Logger
# 5 minutes in seconds
@default_ttl 300
# 5 seconds timeout for Redis operations
@redis_timeout 5000
def child_spec(opts) do
name = Keyword.fetch!(opts, :name)
children = [
{Redix, name: redis_name(name), host: redis_host(), port: redis_port(), password: redis_password()}
]
%{
id: {__MODULE__, name},
type: :supervisor,
start: {Supervisor, :start_link, [children, [strategy: :one_for_one, name: :"#{name}_redis_supervisor"]]}
}
end
@doc """
Get a value from the cache
"""
def get(cache_name, key) do
redis_key = make_redis_key(cache_name, key)
case Redix.command(redis_name(cache_name), ["GET", redis_key], timeout: @redis_timeout) do
{:ok, nil} ->
{:ok, nil}
{:ok, value} ->
{:ok, deserialize(value)}
{:error, reason} ->
Logger.error("Redis GET error for #{redis_key}: #{inspect(reason)}")
{:error, reason}
end
end
@doc """
Put a value in the cache with optional TTL
"""
def put(cache_name, key, value, opts \\ []) do
redis_key = make_redis_key(cache_name, key)
ttl = Keyword.get(opts, :ttl, @default_ttl)
serialized = serialize(value)
case Redix.command(redis_name(cache_name), ["SETEX", redis_key, ttl, serialized], timeout: @redis_timeout) do
{:ok, "OK"} ->
{:ok, true}
{:error, reason} ->
Logger.error("Redis SETEX error for #{redis_key}: #{inspect(reason)}")
{:error, reason}
end
end
@doc """
Delete a value from the cache
"""
def del(cache_name, key) do
redis_key = make_redis_key(cache_name, key)
case Redix.command(redis_name(cache_name), ["DEL", redis_key], timeout: @redis_timeout) do
{:ok, _} ->
{:ok, true}
{:error, reason} ->
Logger.error("Redis DEL error for #{redis_key}: #{inspect(reason)}")
{:error, reason}
end
end
@doc """
Delete multiple keys matching a pattern
"""
def del_pattern(cache_name, pattern) do
redis_pattern = make_redis_key(cache_name, pattern)
# Use SCAN to find keys matching pattern
case scan_keys(cache_name, redis_pattern) do
{:ok, keys} when keys != [] ->
case Redix.command(redis_name(cache_name), ["DEL" | keys], timeout: @redis_timeout) do
{:ok, count} ->
{:ok, count}
{:error, reason} ->
Logger.error("Redis DEL error for pattern #{redis_pattern}: #{inspect(reason)}")
{:error, reason}
end
{:ok, []} ->
{:ok, 0}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Clear all keys for a cache
"""
def clear(cache_name) do
pattern = make_redis_key(cache_name, "*")
case scan_keys(cache_name, pattern) do
{:ok, keys} when keys != [] ->
case Redix.pipeline(redis_name(cache_name), Enum.map(keys, &["DEL", &1]), timeout: @redis_timeout) do
{:ok, _results} ->
{:ok, true}
{:error, reason} ->
Logger.error("Redis CLEAR error for #{cache_name}: #{inspect(reason)}")
{:error, reason}
end
{:ok, []} ->
{:ok, true}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Get cache statistics
"""
def stats(cache_name) do
pattern = make_redis_key(cache_name, "*")
case scan_keys(cache_name, pattern) do
{:ok, keys} ->
{:ok,
%{
size: length(keys),
keys: keys
}}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Check if a key exists
"""
def exists?(cache_name, key) do
redis_key = make_redis_key(cache_name, key)
case Redix.command(redis_name(cache_name), ["EXISTS", redis_key], timeout: @redis_timeout) do
{:ok, 1} -> true
{:ok, 0} -> false
{:error, _} -> false
end
end
@doc """
Get remaining TTL for a key
"""
def ttl(cache_name, key) do
redis_key = make_redis_key(cache_name, key)
case Redix.command(redis_name(cache_name), ["TTL", redis_key], timeout: @redis_timeout) do
# Key doesn't exist
{:ok, -2} -> {:ok, nil}
# Key exists but has no TTL
{:ok, -1} -> {:ok, :infinity}
{:ok, seconds} -> {:ok, seconds}
{:error, reason} -> {:error, reason}
end
end
# Private functions
defp redis_name(cache_name), do: :"#{cache_name}_redis"
defp make_redis_key(cache_name, key) when is_binary(key) do
"aprsme:#{cache_name}:#{key}"
end
defp make_redis_key(cache_name, key) do
"aprsme:#{cache_name}:#{:erlang.phash2(key)}"
end
defp serialize(value) do
:erlang.term_to_binary(value)
end
defp deserialize(binary) when is_binary(binary) do
:erlang.binary_to_term(binary)
rescue
_ -> nil
end
defp scan_keys(cache_name, pattern) do
scan_keys(cache_name, pattern, "0", [])
end
defp scan_keys(cache_name, pattern, cursor, acc) do
case Redix.command(redis_name(cache_name), ["SCAN", cursor, "MATCH", pattern, "COUNT", "100"],
timeout: @redis_timeout
) do
{:ok, [new_cursor, keys]} ->
new_acc = acc ++ keys
if new_cursor == "0" do
{:ok, new_acc}
else
scan_keys(cache_name, pattern, new_cursor, new_acc)
end
{:error, reason} ->
{:error, reason}
end
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

View file

@ -134,11 +134,14 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
@spec valid_coordinates?(any(), any()) :: boolean()
defp valid_coordinates?(lat, lon) when is_number(lat) and is_number(lon) do
lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 and
:math.is_finite(lat) and :math.is_finite(lon)
is_finite(lat) and is_finite(lon)
end
defp valid_coordinates?(_, _), do: false
defp is_finite(n) when is_float(n), do: n != :infinity and n != :neg_infinity and n == n
defp is_finite(n) when is_integer(n), do: true
@doc """
Build packet data list for historical display.
Moved from historical_loader.ex.

View file

@ -223,7 +223,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do
is_number(lat) and is_number(lon) and
lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 and
:math.is_finite(lat) and :math.is_finite(lon)
is_finite(lat) and is_finite(lon)
end)
if length(valid_packets) < length(historical_packets) do
@ -430,4 +430,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do
socket
end
defp is_finite(n) when is_float(n), do: n != :infinity and n != :neg_infinity and n == n
defp is_finite(n) when is_integer(n), do: true
end

View file

@ -50,7 +50,7 @@ defmodule AprsmeWeb.Plugs.ApiCSRF do
reject_request(conn)
session_token ->
if Plug.CSRFProtection.verify_csrf_token(token, session_token) do
if Plug.CSRFProtection.valid_state_and_csrf_token?(session_token, token) do
conn
else
reject_request(conn)

2
vendor/aprs vendored

@ -1 +1 @@
Subproject commit 0d0d21d9e9a342abdb486a4bc8650542df9e38ed
Subproject commit 84a410daf78f720af03701c6ab5b24fdf7cfd0b3