92 lines
3.3 KiB
Markdown
92 lines
3.3 KiB
Markdown
# Architecture Overview
|
||
|
||
## System Purpose
|
||
|
||
Real-time visualization of APRS (Automatic Packet Reporting System) packets on an interactive map. Tracks amateur radio stations, weather data, and station telemetry via APRS-IS network feed.
|
||
|
||
## Technology Stack
|
||
|
||
| Layer | Technology |
|
||
|---|---|
|
||
| Language | Elixir 1.17+ |
|
||
| Web Framework | Phoenix 1.8 + LiveView 1.2 |
|
||
| Database | PostgreSQL + PostGIS |
|
||
| APRS Parsing | `aprs` (Gleam BEAM library) |
|
||
| Map Client | Leaflet (lazy-loaded) |
|
||
| Charts | Chart.js (lazy-loaded) |
|
||
| Metrics | PromEx + Telemetry |
|
||
| Deployment | Kubernetes (ArgoCD), Fly.io, Docker |
|
||
|
||
## High-Level Data Flow
|
||
|
||
```
|
||
APRS-IS Network (TCP)
|
||
│
|
||
▼
|
||
Aprsme.Is (GenServer — TCP connection manager)
|
||
│ raw APRS frames parsed via aprs.parse/1
|
||
▼
|
||
PacketProducer (GenStage — water-mark backpressure)
|
||
│ buffers incoming packets, signals backpressure upstream
|
||
▼
|
||
PacketConsumer pool (GenStage consumers)
|
||
│ sanitize → normalize → insert into Postgres
|
||
│ broadcast to PubSub
|
||
├──► Postgres packets table (daily-partitioned)
|
||
└──► Broadcasting
|
||
├── SpatialPubSub (viewport-filtered, grid-indexed)
|
||
├── StreamingPacketsPubSub (ETS-based, bounds-filtered)
|
||
└── Phoenix.PubSub (per-callsign topics)
|
||
│
|
||
▼
|
||
LiveView clients (MapLive, PacketsLive, InfoLive)
|
||
Mobile WebSocket clients (MobileChannel)
|
||
```
|
||
|
||
## Supervision Tree
|
||
|
||
```
|
||
Aprsme.Application
|
||
├── Aprsme.Repo
|
||
├── Phoenix.PubSub
|
||
├── Aprsme.Cache / DeviceCache / WeatherCache / RegexCache
|
||
├── Aprsme.RateLimiter
|
||
├── Aprsme.PacketPipelineSupervisor
|
||
│ ├── PacketProducer
|
||
│ └── PacketConsumerPool
|
||
│ └── PacketConsumer (×N)
|
||
├── Aprsme.BroadcastTaskSupervisor
|
||
├── Aprsme.Is.IsSupervisor
|
||
│ └── Aprsme.Is
|
||
├── Aprsme.SpatialPubSub
|
||
├── Aprsme.StreamingPacketsPubSub
|
||
├── Aprsme.CleanupScheduler
|
||
├── Aprsme.PartitionManager
|
||
├── Aprsme.PostgresNotifier
|
||
├── Aprsme.ConnectionMonitor
|
||
├── Aprsme.SignalHandler
|
||
├── Aprsme.ShutdownHandler
|
||
├── Aprsme.PromEx
|
||
├── Cluster modules (optional, via libcluster)
|
||
└── AprsmeWeb.Endpoint (Bandit)
|
||
```
|
||
|
||
## Key Design Decisions
|
||
|
||
### Database Partitioning
|
||
The `packets` table is **daily-partitioned** via `PartitionManager`. High ingestion volume makes partitioning essential for query performance and retention cleanup.
|
||
|
||
### Spatial Filtering
|
||
Rather than broadcasting every packet to every client, `SpatialPubSub` maintains a 1° grid spatial index. Clients register their map viewport bounds; only packets intersecting those bounds are delivered.
|
||
|
||
### Backpressure
|
||
`PacketProducer` uses water-mark backpressure (>80% buffer → pause TCP; <30% → resume) to prevent memory exhaustion under load spikes.
|
||
|
||
### Symbol System
|
||
APRS symbols are rendered client-side from sprite sheets. `AprsSymbol` provides server-side symbol table lookups for info pages.
|
||
|
||
### Clustering
|
||
Optional multi-node deployment via `libcluster`. Uses leader election to designate the node that maintains the APRS-IS connection. Packets are relayed to other nodes via `PacketDistributor`.
|
||
|
||
### Lazy Asset Loading
|
||
The map bundle (Leaflet + plugins) and chart bundle (Chart.js) are loaded on-demand only for pages that need them, keeping initial page load small.
|