2.9 KiB
2.9 KiB
Ingestion Pipeline
The packet ingestion pipeline moves raw APRS data from the APRS-IS network into Postgres and broadcasts to connected clients.
Components
Aprsme.Is (TCP Connection)
- File:
lib/aprsme/is/is.ex - Type: GenServer
- Role: Maintains TCP connection to APRS-IS, authenticates with callsign + passcode, receives raw APRS frames
- Parsing: Uses
aprs.parse/1(external Gleam library) to decode APRS frames - Filtering: Configurable APRS-IS filter (by callsign, area, distance) via
Aprsme.Is.LoginParams - Backpressure: Responds to
{:backpressure, :activate | :deactivate}signals fromPacketProducer— sets TCP socket to:passivemode when buffer is full
PacketProducer (GenStage)
- File:
lib/aprsme/packet_producer.ex - Type: GenStage producer
- Buffer: Erlang
:queuewith water-mark backpressure- High water (>80% capacity): signals
Aprsme.Isto activate backpressure - Low water (<30% capacity): signals
Aprsme.Isto deactivate backpressure
- High water (>80% capacity): signals
- Demand-driven: Only emits packets when consumers are ready
PacketConsumer (GenStage Consumer)
- File:
lib/aprsme/packet_consumer.ex - Type: GenStage consumer (pooled via
PacketConsumerPool) - Processing pipeline per packet:
- Sanitize —
PacketSanitizer.sanitize_packet/1(overflow prevention) - Normalize —
EncodingUtilsconversions (UTF-8, latin1, floats, decimals) - Extract position — lat/lon → PostGIS geometry
- Extract device —
DeviceParser.extract_device_id/1 - Filter fields —
PacketFieldWhitelisttrims to schema columns - Insert —
Repo.insert_allwithon_conflict: :nothing(idempotent) - Broadcast — async dispatch to PubSub systems
- Sanitize —
Broadcasting After Insert
After successful insert, each packet is broadcast to three PubSub layers:
- StreamingPacketsPubSub — ETS-based, clients subscribe with bounds
- SpatialPubSub — Grid-indexed (1° cells), viewport-filtered delivery
- Phoenix.PubSub — Legacy per-topic broadcasts:
"postgres:aprsme_packets"— global packet stream"packets:#{callsign}"— per-callsign updates"weather:#{callsign}"— per-weather-station updates
Broadcast execution is async via BroadcastTaskSupervisor.
Error Handling
Packets that fail parsing or validation are stored in badpackets table via Packets.store_bad_packet/2 with error type/message, viewable at /badpackets page.
Configuration
- APRS-IS server, callsign, passcode set via application config
- Consumer pool size configurable per environment
- Retention period configurable via
CleanupScheduler
Performance Notes
PreparedQueriesmodule caches frequently-used query plansInsertOptimizerbatch-tunes insert operationsPacketConsumerusesinsert_all(not individual inserts) for throughput- Daily table partitioning via
PartitionManagerkeeps index sizes manageable