aprs.me/lib/aprsme/application.ex
Graham McIntire 781fdd88a3
Remove dead modules, unused deps, and commented-out code
Delete 5 unused modules: Presence (untracked), LiveView ShutdownHandler
(unused mixin), RateLimiterWrapper (pointless delegation), Passcode
(only used by own test), MapLive.Utils (pure delegates).

Fix ShutdownHandler to not call deleted Presence. Inline ParamUtils
at call sites. Remove cachex dep and 27 stale entries from mix.lock.
Clean commented-out Exq/Redis config from application.ex.
2026-02-20 08:09:22 -06:00

196 lines
6 KiB
Elixir

defmodule Aprsme.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
# Configure Oban for background jobs
@impl true
def start(_type, _args) do
# Initialize deployment timestamp
Aprsme.Release.init()
# Run migrations on startup
migrate()
children = [
# Start the Telemetry supervisor
AprsmeWeb.Telemetry,
# Start the Ecto repository
Aprsme.Repo,
# Start the PubSub system
pubsub_config(),
# Start Redis-based rate limiter and caches (only if Redis is available)
# Start circuit breaker
Aprsme.CircuitBreaker,
# Start regex cache for performance
Aprsme.RegexCache,
# Start device cache manager
Aprsme.DeviceCache,
# Start broadcast task supervisor for async operations
Aprsme.BroadcastTaskSupervisor,
# Start spatial PubSub for viewport-based filtering
Aprsme.SpatialPubSub,
# Start global streaming packets PubSub
Aprsme.StreamingPacketsPubSub,
# Start the Endpoint (http/https)
AprsmeWeb.Endpoint,
# Start a worker by calling: Aprsme.Worker.start_link(arg)
# {Aprsme.Worker, arg}
{Registry, keys: :duplicate, name: Registry.PubSub, partitions: System.schedulers_online()},
# Start cleanup scheduler for periodic packet cleanup
Aprsme.CleanupScheduler,
Aprsme.PostgresNotifier,
# Start deployment notifier
Aprsme.DeploymentNotifier,
# Start the packet processing pipeline
Aprsme.PacketPipelineSupervisor
]
# Skip packet pipeline in test to avoid Sandbox ownership errors
children =
if Application.get_env(:aprsme, :env) == :test do
List.delete(children, Aprsme.PacketPipelineSupervisor)
else
children
end
children = children ++ redis_children()
# Add shutdown handlers at the end, after everything else is started
children =
children ++
[
Aprsme.SignalHandler,
Aprsme.ShutdownHandler
]
children = maybe_add_cluster_components(children)
children = maybe_add_is_supervisor(children, Application.get_env(:aprsme, :env))
# Exq is now started automatically via config, not in supervision tree
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Aprsme.Supervisor]
{:ok, sup} = Supervisor.start_link(children, opts)
# Now that the Repo is started, run the refresh in a background task
# Skip in test environment to avoid DBConnection.OwnershipError
env = Application.get_env(:aprsme, :env)
if env != :test do
Task.start(fn -> Aprsme.DeviceIdentification.maybe_refresh_devices() end)
end
{:ok, sup}
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
@impl true
def config_change(changed, _new, removed) do
AprsmeWeb.Endpoint.config_change(changed, removed)
:ok
end
defp migrate do
auto_migrate = Application.get_env(:aprsme, :auto_migrate, true)
cluster_enabled = Application.get_env(:aprsme, :cluster_enabled, false)
# In cluster mode, prefer init containers or manual migration
# to avoid race conditions between nodes
if auto_migrate and not cluster_enabled do
do_migrate(true)
else
require Logger
if cluster_enabled do
Logger.info("Skipping auto-migration in cluster mode")
else
Logger.info("Auto-migration disabled")
end
end
# Gettext translations are automatically compiled during Mix compilation
rescue
error ->
require Logger
Logger.error("Failed to run migrations: #{inspect(error)}")
# Don't crash the application, just log the error
:ok
end
defp maybe_add_cluster_components(children) do
if Application.get_env(:aprsme, :cluster_enabled, false) do
topologies = Application.get_env(:libcluster, :topologies, [])
# Packet receiver for distributed packets on non-leader nodes
cluster_children =
if topologies == [] do
[]
# libcluster supervisor
# Dynamic supervisor for processes managed by cluster leader
# Leader election process
# Connection manager that starts/stops APRS-IS based on leadership
else
[
{Cluster.Supervisor, [topologies, [name: Aprsme.ClusterSupervisor]]},
Aprsme.DynamicSupervisor,
Aprsme.Cluster.LeaderElection,
Aprsme.Cluster.ConnectionManager,
Aprsme.Cluster.PacketReceiver,
Aprsme.ConnectionMonitor
]
end
children ++ cluster_children
else
children
end
end
defp maybe_add_is_supervisor(children, env) do
disable_connection = Application.get_env(:aprsme, :disable_aprs_connection, false)
cluster_enabled = Application.get_env(:aprsme, :cluster_enabled, false)
# Only add Is.IsSupervisor directly if clustering is disabled
if env in [:prod, :dev] and not disable_connection and not cluster_enabled do
children ++ [Aprsme.Is.IsSupervisor]
else
children
end
end
defp do_migrate(true) do
require Logger
Logger.info("Running database migrations...")
Aprsme.Release.migrate()
Logger.info("Database migrations completed")
end
defp pubsub_config do
{Phoenix.PubSub, name: Aprsme.PubSub}
end
defp redis_children do
require Logger
Logger.info("Starting ETS-based caching and rate limiting")
# 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])
:ets.new(:aprsme, [:set, :public, :named_table, write_concurrency: true])
:ets.insert(:aprsme, {:message_number, 0})
[
# ETS-based rate limiter
Aprsme.RateLimiter
]
end
end