Remove Redis/Cachex dependency, use pure ETS for caching

- Replace Cachex with native ETS (Erlang Term Storage) in Cache module
- Remove Redis-based caching logic from application.ex
- Always initialize ETS tables for device_cache, query_cache, symbol_cache
- Update StatusLive to use Aprsme.Cache instead of Cachex directly
- Fix crash on startup when REDIS_URL was set but Redis not used

This fixes the no_cache error that was causing pods to crash.
This commit is contained in:
Graham McIntire 2025-10-24 17:12:38 -05:00
parent 18d95b9a1f
commit ae14b120fa
No known key found for this signature in database
3 changed files with 54 additions and 34 deletions

View file

@ -235,24 +235,18 @@ defmodule Aprsme.Application do
# end
defp redis_children do
if System.get_env("REDIS_URL") do
require Logger
require Logger
Logger.info("Starting Redis-based caching and rate limiting")
Logger.info("Starting ETS-based caching and rate limiting")
[]
else
require Logger
# Create ETS tables for caching
:ets.new(:query_cache, [:set, :public, :named_table, read_concurrency: true])
:ets.new(:device_cache, [:set, :public, :named_table, read_concurrency: true])
:ets.new(:symbol_cache, [:set, :public, :named_table, read_concurrency: true])
Logger.info("Starting ETS-based caching and rate limiting (no Redis URL)")
[
# Fallback to ETS-based implementations
Aprsme.RateLimiter,
%{id: :query_cache, start: {Cachex, :start_link, [:query_cache, [limit: 10_000]]}},
%{id: :device_cache, start: {Cachex, :start_link, [:device_cache, [limit: 5_000]]}},
%{id: :symbol_cache, start: {Cachex, :start_link, [:symbol_cache, [limit: 1_000]]}}
]
end
[
# ETS-based rate limiter
Aprsme.RateLimiter
]
end
end

View file

@ -1,59 +1,85 @@
defmodule Aprsme.Cache do
@moduledoc """
Cache abstraction layer that works with both Cachex and RedisCache.
Provides a unified API regardless of the underlying implementation.
Cache abstraction layer using ETS (Erlang Term Storage).
Provides a unified API for in-memory caching.
"""
@doc """
Get a value from cache
"""
def get(cache_name, key) do
Cachex.get(cache_name, key)
case :ets.lookup(cache_name, key) do
[{^key, value}] -> {:ok, value}
[] -> {:ok, nil}
end
rescue
ArgumentError -> {:error, :no_cache}
end
@doc """
Put a value in cache with optional TTL
Put a value in cache with optional TTL (TTL not implemented for ETS)
"""
def put(cache_name, key, value, opts \\ []) do
Cachex.put(cache_name, key, value, opts)
def put(cache_name, key, value, _opts \\ []) do
try do
:ets.insert(cache_name, {key, value})
{:ok, true}
rescue
ArgumentError -> {:error, :no_cache}
end
end
@doc """
Delete a key from cache
"""
def del(cache_name, key) do
Cachex.del(cache_name, key)
try do
:ets.delete(cache_name, key)
{:ok, true}
rescue
ArgumentError -> {:error, :no_cache}
end
end
@doc """
Clear all keys from cache
"""
def clear(cache_name) do
Cachex.clear(cache_name)
try do
:ets.delete_all_objects(cache_name)
{:ok, true}
rescue
ArgumentError -> {:error, :no_cache}
end
end
@doc """
Get cache statistics
Get cache statistics (simplified for ETS)
"""
def stats(cache_name) do
Cachex.stats(cache_name)
try do
info = :ets.info(cache_name)
{:ok, %{size: Keyword.get(info, :size, 0)}}
rescue
ArgumentError -> {:error, :no_cache}
end
end
@doc """
Check if key exists
"""
def exists?(cache_name, key) do
case Cachex.exists?(cache_name, key) do
{:ok, exists?} -> exists?
{:error, _reason} -> false
try do
:ets.member(cache_name, key)
rescue
ArgumentError -> false
end
end
@doc """
Get TTL for a key
Get TTL for a key (not supported in ETS, always returns nil)
"""
def ttl(cache_name, key) do
Cachex.ttl(cache_name, key)
def ttl(_cache_name, _key) do
{:ok, nil}
end
# Helper functions - no longer needed as we only use Cachex

View file

@ -395,14 +395,14 @@ defmodule AprsmeWeb.StatusLive.Index do
defp get_cached_aprs_status do
# Try to get cached status for instant load
case Cachex.get(:query_cache, "aprs_status") do
case Aprsme.Cache.get(:query_cache, "aprs_status") do
{:ok, status} when not is_nil(status) ->
status
_ ->
# Fallback to direct query if cache miss
status = get_aprs_status()
Cachex.put(:query_cache, "aprs_status", status, ttl: to_timeout(second: 5))
Aprsme.Cache.put(:query_cache, "aprs_status", status)
status
end
end