From 8937c1fb572fa1fc2d0b757fa678888b5893c2b3 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 22 Mar 2026 15:58:14 -0500 Subject: [PATCH] fix: handle APRS status DB ownership exits --- lib/aprsme/is/is.ex | 20 ++++++++++++++++---- test/aprsme/is_test.exs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/aprsme/is/is.ex b/lib/aprsme/is/is.ex index b0b0868..cdec341 100644 --- a/lib/aprsme/is/is.ex +++ b/lib/aprsme/is/is.ex @@ -129,6 +129,7 @@ defmodule Aprsme.Is do # GenServer is not running (disconnected) server = Application.get_env(:aprsme, :aprs_is_server, nil) port = Application.get_env(:aprsme, :aprs_is_port, 14_580) + {stored_packet_count, oldest_packet_timestamp} = safe_packet_storage_stats() %{ connected: false, @@ -139,8 +140,8 @@ defmodule Aprsme.Is do login_id: Application.get_env(:aprsme, :aprs_is_login_id, "W5ISP"), filter: Application.get_env(:aprsme, :aprs_is_default_filter, "r/33/-96/100"), packet_stats: default_packet_stats(), - stored_packet_count: Aprsme.Packets.get_total_packet_count(), - oldest_packet_timestamp: Aprsme.Packets.get_oldest_packet_timestamp() + stored_packet_count: stored_packet_count, + oldest_packet_timestamp: oldest_packet_timestamp } _pid -> @@ -151,6 +152,7 @@ defmodule Aprsme.Is do # GenServer exists but not responding server = Application.get_env(:aprsme, :aprs_is_server, ~c"rotate.aprs2.net") port = Application.get_env(:aprsme, :aprs_is_port, 14_580) + {stored_packet_count, oldest_packet_timestamp} = safe_packet_storage_stats() %{ connected: false, @@ -161,13 +163,23 @@ defmodule Aprsme.Is do login_id: Application.get_env(:aprsme, :aprs_is_login_id, "W5ISP"), filter: Application.get_env(:aprsme, :aprs_is_default_filter, "r/33/-96/100"), packet_stats: default_packet_stats(), - stored_packet_count: Aprsme.Packets.get_total_packet_count(), - oldest_packet_timestamp: Aprsme.Packets.get_oldest_packet_timestamp() + stored_packet_count: stored_packet_count, + oldest_packet_timestamp: oldest_packet_timestamp } end end end + defp safe_packet_storage_stats do + {Aprsme.Packets.get_total_packet_count(), Aprsme.Packets.get_oldest_packet_timestamp()} + rescue + DBConnection.OwnershipError -> + {0, nil} + catch + :exit, _ -> + {0, nil} + end + def set_filter(filter_string), do: send_message("#filter #{filter_string}") def list_active_filters, do: send_message("#filter?") diff --git a/test/aprsme/is_test.exs b/test/aprsme/is_test.exs index 1e77835..15b74c5 100644 --- a/test/aprsme/is_test.exs +++ b/test/aprsme/is_test.exs @@ -7,6 +7,8 @@ defmodule Aprsme.IsTest do import ExUnit.CaptureLog + alias Ecto.Adapters.SQL.Sandbox + # Helper to build a default GenServer state for direct callback testing defp build_state(overrides \\ %{}) do timer = Process.send_after(self(), :noop_timer, to_timeout(minute: 5)) @@ -519,6 +521,38 @@ defmodule Aprsme.IsTest do assert status.login_id == "TEST" assert status.filter == "r/33/-96/100" end + + test "returns fallback packet storage stats when called from a non-owner process" do + parent = self() + + owner = + spawn(fn -> + :ok = Sandbox.checkout(Aprsme.Repo) + send(parent, :sandbox_owner_ready) + + receive do + :stop -> :ok + end + end) + + assert_receive :sandbox_owner_ready + + task = + Task.async(fn -> + receive do + :query_status -> Aprsme.Is.get_status() + end + end) + + Sandbox.allow(Aprsme.Repo, owner, task.pid) + send(owner, :stop) + send(task.pid, :query_status) + status = Task.await(task) + + assert status.connected == false + assert status.stored_packet_count == 0 + assert status.oldest_packet_timestamp == nil + end end describe "buffer line extraction via TCP data" do