Mobile callsign subscribers go silent for the rest of the session after a SpatialPubSub restart #54

Open
opened 2026-11-03 18:52:29 +00:00 by skippy-bot · 0 comments

Problem

Aprsme.SpatialPubSub is a plain supervised child with in-memory state. If it dies and restarts, every connected socket keeps its Phoenix PubSub subscription but has no registration, so packets stop arriving with no error frame and no disconnect, which means the client's reconnect logic never fires. A client that pushes subscribe_callsign again gets status ok plus replayed history and then silence again, because the handler short-circuits on the socket assign. A bounds subscriber only recovers if the user happens to pan, since update_bounds is the one path that handles {:error, :not_registered}.

Evidence

At 2f150f66:

  • lib/aprsme/application.ex:43 starts Aprsme.SpatialPubSub with no persistence, and lib/aprsme/spatial_pubsub.ex:89 rebuilds %{clients: %{}, spatial_index: %{}} on init.
  • lib/aprsme_web/channels/mobile_channel.ex:810 returns {:ok, socket} when callsign_subscription_active?/1 is true, without re-registering.
  • lib/aprsme_web/channels/mobile_channel.ex:774 is the {:error, :not_registered} recovery clause, reached only from the bounds update path.
  • lib/aprsme/spatial_pubsub.ex:525 treats a missing grid key as an empty client list, so an unregistered socket matches nothing.

Expected

A subscribe, or a registration check on the live packet path, re-establishes the registration, so a supervisor restart costs at most one message rather than the rest of the session.

How to verify

In test/aprsme_web/channels/mobile_channel_test.exs, kill Aprsme.SpatialPubSub, wait for the restart, push subscribe_callsign again, broadcast a packet, and assert a push arrives. Today assert_push fails.

Acceptance criteria

  • A callsign subscriber resumes live packets after a SpatialPubSub restart, whether or not the client resubscribes.
  • The bounds subscriber recovers on the next packet, not only on a pan.
  • A test kills the process and asserts delivery resumes.

Effort: S
Lens: reliability

## Problem `Aprsme.SpatialPubSub` is a plain supervised child with in-memory state. If it dies and restarts, every connected socket keeps its Phoenix PubSub subscription but has no registration, so packets stop arriving with no error frame and no disconnect, which means the client's reconnect logic never fires. A client that pushes `subscribe_callsign` again gets `status ok` plus replayed history and then silence again, because the handler short-circuits on the socket assign. A bounds subscriber only recovers if the user happens to pan, since `update_bounds` is the one path that handles `{:error, :not_registered}`. ## Evidence At `2f150f66`: - `lib/aprsme/application.ex:43` starts `Aprsme.SpatialPubSub` with no persistence, and `lib/aprsme/spatial_pubsub.ex:89` rebuilds `%{clients: %{}, spatial_index: %{}}` on init. - `lib/aprsme_web/channels/mobile_channel.ex:810` returns `{:ok, socket}` when `callsign_subscription_active?/1` is true, without re-registering. - `lib/aprsme_web/channels/mobile_channel.ex:774` is the `{:error, :not_registered}` recovery clause, reached only from the bounds update path. - `lib/aprsme/spatial_pubsub.ex:525` treats a missing grid key as an empty client list, so an unregistered socket matches nothing. ## Expected A subscribe, or a registration check on the live packet path, re-establishes the registration, so a supervisor restart costs at most one message rather than the rest of the session. ## How to verify In `test/aprsme_web/channels/mobile_channel_test.exs`, kill `Aprsme.SpatialPubSub`, wait for the restart, push `subscribe_callsign` again, broadcast a packet, and assert a push arrives. Today `assert_push` fails. ## Acceptance criteria - [ ] A callsign subscriber resumes live packets after a SpatialPubSub restart, whether or not the client resubscribes. - [ ] The bounds subscriber recovers on the next packet, not only on a pan. - [ ] A test kills the process and asserts delivery resumes. **Effort:** S **Lens:** reliability
Sign in to join this conversation.
No description provided.