cleanup and remove redis
This commit is contained in:
parent
6acc007360
commit
cc4578653f
7 changed files with 21 additions and 324 deletions
|
|
@ -240,14 +240,7 @@ defmodule Aprsme.Application do
|
||||||
|
|
||||||
Logger.info("Starting Redis-based caching and rate limiting")
|
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
|
else
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,70 +8,44 @@ defmodule Aprsme.Cache do
|
||||||
Get a value from cache
|
Get a value from cache
|
||||||
"""
|
"""
|
||||||
def get(cache_name, key) do
|
def get(cache_name, key) do
|
||||||
if using_redis?() do
|
Cachex.get(cache_name, key)
|
||||||
Aprsme.RedisCache.get(cache_name, key)
|
|
||||||
else
|
|
||||||
Cachex.get(cache_name, key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Put a value in cache with optional TTL
|
Put a value in cache with optional TTL
|
||||||
"""
|
"""
|
||||||
def put(cache_name, key, value, opts \\ []) do
|
def put(cache_name, key, value, opts \\ []) do
|
||||||
if using_redis?() do
|
Cachex.put(cache_name, key, value, opts)
|
||||||
# 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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Delete a key from cache
|
Delete a key from cache
|
||||||
"""
|
"""
|
||||||
def del(cache_name, key) do
|
def del(cache_name, key) do
|
||||||
if using_redis?() do
|
Cachex.del(cache_name, key)
|
||||||
Aprsme.RedisCache.del(cache_name, key)
|
|
||||||
else
|
|
||||||
Cachex.del(cache_name, key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Clear all keys from cache
|
Clear all keys from cache
|
||||||
"""
|
"""
|
||||||
def clear(cache_name) do
|
def clear(cache_name) do
|
||||||
if using_redis?() do
|
Cachex.clear(cache_name)
|
||||||
Aprsme.RedisCache.clear(cache_name)
|
|
||||||
else
|
|
||||||
Cachex.clear(cache_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get cache statistics
|
Get cache statistics
|
||||||
"""
|
"""
|
||||||
def stats(cache_name) do
|
def stats(cache_name) do
|
||||||
if using_redis?() do
|
Cachex.stats(cache_name)
|
||||||
Aprsme.RedisCache.stats(cache_name)
|
|
||||||
else
|
|
||||||
Cachex.stats(cache_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Check if key exists
|
Check if key exists
|
||||||
"""
|
"""
|
||||||
def exists?(cache_name, key) do
|
def exists?(cache_name, key) do
|
||||||
if using_redis?() do
|
case Cachex.exists?(cache_name, key) do
|
||||||
Aprsme.RedisCache.exists?(cache_name, key)
|
{:ok, exists?} -> exists?
|
||||||
else
|
{:error, _reason} -> false
|
||||||
case Cachex.exists?(cache_name, key) do
|
|
||||||
{:ok, exists?} -> exists?
|
|
||||||
{:error, _reason} -> false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -79,35 +53,10 @@ defmodule Aprsme.Cache do
|
||||||
Get TTL for a key
|
Get TTL for a key
|
||||||
"""
|
"""
|
||||||
def ttl(cache_name, key) do
|
def ttl(cache_name, key) do
|
||||||
if using_redis?() do
|
Cachex.ttl(cache_name, key)
|
||||||
Aprsme.RedisCache.ttl(cache_name, key)
|
|
||||||
else
|
|
||||||
Cachex.ttl(cache_name, key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions - no longer needed as we only use Cachex
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Convert timeout keyword list to milliseconds
|
Convert timeout keyword list to milliseconds
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -134,11 +134,14 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
||||||
@spec valid_coordinates?(any(), any()) :: boolean()
|
@spec valid_coordinates?(any(), any()) :: boolean()
|
||||||
defp valid_coordinates?(lat, lon) when is_number(lat) and is_number(lon) do
|
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
|
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
|
end
|
||||||
|
|
||||||
defp valid_coordinates?(_, _), do: false
|
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 """
|
@doc """
|
||||||
Build packet data list for historical display.
|
Build packet data list for historical display.
|
||||||
Moved from historical_loader.ex.
|
Moved from historical_loader.ex.
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do
|
||||||
|
|
||||||
is_number(lat) and is_number(lon) and
|
is_number(lat) and is_number(lon) and
|
||||||
lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 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)
|
end)
|
||||||
|
|
||||||
if length(valid_packets) < length(historical_packets) do
|
if length(valid_packets) < length(historical_packets) do
|
||||||
|
|
@ -430,4 +430,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do
|
||||||
|
|
||||||
socket
|
socket
|
||||||
end
|
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
|
end
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ defmodule AprsmeWeb.Plugs.ApiCSRF do
|
||||||
reject_request(conn)
|
reject_request(conn)
|
||||||
|
|
||||||
session_token ->
|
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
|
conn
|
||||||
else
|
else
|
||||||
reject_request(conn)
|
reject_request(conn)
|
||||||
|
|
|
||||||
2
vendor/aprs
vendored
2
vendor/aprs
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0d0d21d9e9a342abdb486a4bc8650542df9e38ed
|
Subproject commit 84a410daf78f720af03701c6ab5b24fdf7cfd0b3
|
||||||
Loading…
Add table
Reference in a new issue