Converts the inline state maps in Is, PacketConsumer, LeaderElection, and
CircuitBreaker into structs with @type t specs. Enforced keys catch typos
on state updates and give dialyzer a concrete type to check against
instead of map() in every handle_* clause.
Is' nested login_params and packet_stats are extracted to their own
modules (Aprsme.Is.LoginParams, Aprsme.Is.PacketStats) rather than
defined inline, per the project's no-nested-modules rule.
Drop the pre-insert aprs_messages broadcast in Is.dispatch (subscribers
already get a richer payload via postgres:aprsme_packets after insert).
Switch PacketsLive.CallsignView to the per-callsign packets:<CS> topic
so it only receives relevant packets instead of filtering every one.
In PacketConsumer: reuse the received_at stamped in Is.dispatch instead
of calling DateTime.utc_now/0 per packet; drop the duplicate struct_to_map
pass (Is.dispatch already handled it); fold coordinate validation and
Geo.Point construction into set_lat_lon + create_location_geometry so
coords are normalized once; extract device_identifier from the already-
normalized attrs; pre-build the broadcast payload once (identifier pick,
lat/lon aliases, routing callsign) so the async broadcast task no longer
does Map.drop/Map.merge per packet.
Rewrite PacketSanitizer.sanitize_packet / sanitize_data_map with Map.new/2
instead of Enum.reduce + Map.put — one allocation per packet instead of N.
Also compute has_weather in Packet.changeset/2 so direct-changeset inserts
(tests, backfills) populate it the same way the GenStage pipeline does.
When the PacketProducer buffer crosses 80% capacity, signal Aprsme.Is
to switch its TCP socket to passive mode, leveraging TCP flow control
to slow APRS-IS intake. Resume active mode when buffer drains below 30%.
A 30-second safety valve timer auto-resumes if backpressure gets stuck.
Disconnect/reconnect/terminate paths all clear backpressure state.
- Handle login send failure in AprsIsConnection instead of crashing
- Close leaked sockets on login failure in Aprsme.Is (init + reconnect)
- Keep rescheduling keepalive timer when disconnected to prevent permanent loss
- Use supervised BroadcastTaskSupervisor instead of unsupervised Task.start
for packet broadcasting
- Remove racy Process.alive? check in StreamingPacketsPubSub; send/2 to
dead PIDs is a no-op, :DOWN monitors handle cleanup
- Add dateline wrapping to SpatialPubSub.point_in_bounds? matching
StreamingPacketsPubSub behavior
- Add bad_packets table cleanup to PacketCleanupWorker
- Add TODO.md with remaining improvements
- Initialize :aprsme ETS table in application.ex with message_number
counter; was missing, causing crash in Is.send_message/1
- Trigger reconnect on gen_tcp.send failure instead of leaking the
dead socket with active timers
- Remove cluster-only env vars from deployment (POD_IP, POD_NAME,
POD_NAMESPACE, RELEASE_DISTRIBUTION, RELEASE_NODE) since
CLUSTER_ENABLED=false
- Remove redundant APRS_PASSCODE secret ref now that APRS_PASSWORD
is set as plaintext