From 2b3cc3f75e6d384e2c1f6a4e7ace75ba903dbbb9 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 16 Jun 2025 08:25:53 -0500 Subject: [PATCH] change packet retention to a year --- lib/aprs/is/is.ex | 19 ------- lib/aprs/packets.ex | 24 +++++++- lib/aprs/passcode.ex | 2 +- lib/aprs/scheduled/packet_cleanup.ex | 68 ----------------------- lib/aprs/workers/packet_cleanup_worker.ex | 61 ++++++++++++++++++-- test/aprs/passcode_test.exs | 9 +-- 6 files changed, 84 insertions(+), 99 deletions(-) delete mode 100644 lib/aprs/scheduled/packet_cleanup.ex diff --git a/lib/aprs/is/is.ex b/lib/aprs/is/is.ex index 17729c2..103b38d 100644 --- a/lib/aprs/is/is.ex +++ b/lib/aprs/is/is.ex @@ -38,25 +38,6 @@ defmodule Aprs.Is do last_second_timestamp: System.system_time(:second) } - # Set up ets tables - - # with {:ok, :aprs} <- :ets.file2tab(:erlang.binary_to_list("priv/aprs.ets")), - # {:ok, :aprs_messages} <- :ets.file2tab(:erlang.binary_to_list("priv/aprs_messages.ets")) do - # else - # {:error, _reason} -> - # Logger.debug("Ets files not found. Creating new ETS tables.") - # # Create new ETS tables - # :ets.new(:aprs, [:set, :protected, :named_table]) - # :ets.new(:aprs_messages, [:bag, :protected, :named_table]) - - # # Write them to file here in case genserver is brutally terminated - # :ets.tab2file(:aprs, :erlang.binary_to_list("priv/aprs.ets")) - # :ets.tab2file(:aprs_messages, :erlang.binary_to_list("priv/aprs_messages.ets")) - - # _ -> - # Logger.error("Unable to set up ETS tables for message storage.") - # end - with {:ok, socket} <- connect_to_aprs_is(server, port), :ok <- send_login_string(socket, aprs_user_id, aprs_passcode, default_filter) do timer = create_timer(@aprs_timeout) diff --git a/lib/aprs/packets.ex b/lib/aprs/packets.ex index c8ee24d..c5fc823 100644 --- a/lib/aprs/packets.ex +++ b/lib/aprs/packets.ex @@ -270,11 +270,11 @@ defmodule Aprs.Packets do Configure packet retention policy. Packets are retained based on these rules: - - Default retention is 30 days (configurable via :packet_retention_days) + - Default retention is 365 days (1 year) (configurable via :packet_retention_days) - Returns the number of packets deleted """ def clean_old_packets do - retention_days = Application.get_env(:aprs, :packet_retention_days, 30) + retention_days = Application.get_env(:aprs, :packet_retention_days, 365) cutoff_time = DateTime.add(DateTime.utc_now(), -retention_days * 86_400, :second) {deleted_count, _} = Repo.delete_all(from(p in Packet, where: p.received_at < ^cutoff_time)) @@ -282,6 +282,26 @@ defmodule Aprs.Packets do deleted_count end + @doc """ + Clean packets older than a specific number of days. + + This function allows for more granular cleanup operations by specifying + the exact age threshold for packet deletion. + + ## Parameters + - `days` - Number of days to retain packets (packets older than this will be deleted) + + ## Returns + - Number of packets deleted + """ + def clean_packets_older_than(days) when is_integer(days) and days > 0 do + cutoff_time = DateTime.add(DateTime.utc_now(), -days * 86_400, :second) + + {deleted_count, _} = Repo.delete_all(from(p in Packet, where: p.received_at < ^cutoff_time)) + + deleted_count + end + # Helper to convert various types to float defp to_float(value) when is_float(value), do: value defp to_float(value) when is_integer(value), do: value * 1.0 diff --git a/lib/aprs/passcode.ex b/lib/aprs/passcode.ex index d6b2c94..07d0cb4 100644 --- a/lib/aprs/passcode.ex +++ b/lib/aprs/passcode.ex @@ -36,7 +36,7 @@ defmodule Aprs.Passcode do hash = Enum.reduce(0..div(length(chars) - 1, 2), hash, fn i, acc -> current_char = Enum.at(chars, i * 2) - next_char = if i * 2 + 1 < length(chars), do: Enum.at(chars, i * 2 + 1), else: nil + next_char = if i * 2 + 1 < length(chars), do: Enum.at(chars, i * 2 + 1) step1 = Bitwise.bxor(acc, Bitwise.bsl(current_char, 8)) step2 = diff --git a/lib/aprs/scheduled/packet_cleanup.ex b/lib/aprs/scheduled/packet_cleanup.ex deleted file mode 100644 index e8a02d7..0000000 --- a/lib/aprs/scheduled/packet_cleanup.ex +++ /dev/null @@ -1,68 +0,0 @@ -defmodule Aprs.Scheduled.PacketCleanup do - @moduledoc """ - Scheduled task to clean up old APRS packet data. - - This module is responsible for: - 1. Removing packets older than the retention period (30 days by default) - 2. Logging statistics about the cleanup operation - """ - - use GenServer - - alias Aprs.Packets - - require Logger - - # Run cleanup once per day - @cleanup_interval_ms 24 * 60 * 60 * 1000 - - def start_link(_opts) do - GenServer.start_link(__MODULE__, %{}) - end - - @impl true - def init(state) do - # Schedule first cleanup - schedule_cleanup() - {:ok, state} - end - - @impl true - def handle_info(:cleanup, state) do - # Run the cleanup - _cleanup_count = perform_cleanup() - - # Schedule next cleanup - schedule_cleanup() - - {:noreply, state} - end - - defp perform_cleanup do - Logger.info("Starting scheduled APRS packet cleanup") - - # Count packets before cleanup for statistics - _total_before = count_total_packets() - - # Perform the cleanup - deleted_count = Packets.clean_old_packets() - - # Log results - retention_days = Application.get_env(:aprs, :packet_retention_days, 30) - Logger.info("APRS packet cleanup complete: removed #{deleted_count} packets older than #{retention_days} days") - - # Return deleted count - deleted_count - end - - defp count_total_packets do - # Import modules needed for database operations - - Aprs.Repo.aggregate(Aprs.Packet, :count, :id) - end - - defp schedule_cleanup do - # Schedule next cleanup - Process.send_after(self(), :cleanup, @cleanup_interval_ms) - end -end diff --git a/lib/aprs/workers/packet_cleanup_worker.ex b/lib/aprs/workers/packet_cleanup_worker.ex index e6d93e4..0ae0fef 100644 --- a/lib/aprs/workers/packet_cleanup_worker.ex +++ b/lib/aprs/workers/packet_cleanup_worker.ex @@ -3,9 +3,16 @@ defmodule Aprs.Workers.PacketCleanupWorker do Oban worker for cleaning up old APRS packet data. This worker is responsible for: - 1. Removing packets older than the retention period (30 days by default) - 2. Removing old packets from the displayed map (older than 1 hour) - 3. Logging statistics about the cleanup operation + 1. Removing packets older than the retention period (1 year by default) + 2. Providing granular cleanup with custom age parameters + 3. Logging statistics about cleanup operations + + ## Functions + - `perform/1` - Standard cleanup using configured retention period + - `cleanup_packets_older_than/1` - Cleanup packets older than specified days + - `schedule_cleanup/0` - Schedule standard cleanup job + - `schedule_cleanup_for_age/1` - Schedule cleanup with custom age parameter + - `schedule_daily_cleanup/0` - Schedule daily cleanup at midnight UTC """ use Oban.Worker, queue: :maintenance, max_attempts: 3 @@ -19,17 +26,33 @@ defmodule Aprs.Workers.PacketCleanupWorker do require Logger @impl Oban.Worker + def perform(%Oban.Job{args: %{"cleanup_days" => days}}) when is_integer(days) do + Logger.info("Starting scheduled APRS packet cleanup for packets older than #{days} days") + + # Count packets before cleanup for statistics + _total_before = count_total_packets() + + # Perform the cleanup of old packets with custom age + deleted_count = Packets.clean_packets_older_than(days) + + # Log results + Logger.info("APRS packet cleanup complete: removed #{deleted_count} packets older than #{days} days") + + # Return success + :ok + end + def perform(%Oban.Job{args: _args}) do Logger.info("Starting scheduled APRS packet cleanup") # Count packets before cleanup for statistics _total_before = count_total_packets() - # Perform the cleanup of old packets (older than 30 days) + # Perform the cleanup of old packets (older than 1 year by default) deleted_count = cleanup_old_packets() # Log results - retention_days = Application.get_env(:aprs, :packet_retention_days, 30) + retention_days = Application.get_env(:aprs, :packet_retention_days, 365) Logger.info("APRS packet cleanup complete: removed #{deleted_count} packets older than #{retention_days} days") # Return success @@ -43,6 +66,13 @@ defmodule Aprs.Workers.PacketCleanupWorker do |> Oban.insert() end + # Schedule cleanup with custom age parameter + def schedule_cleanup_for_age(days) when is_integer(days) and days > 0 do + %{"cleanup_days" => days} + |> Oban.Job.new(worker: __MODULE__, queue: :maintenance) + |> Oban.insert() + end + # Schedule this job to run daily at midnight UTC def schedule_daily_cleanup do # Calculate when the next midnight UTC is @@ -74,4 +104,25 @@ defmodule Aprs.Workers.PacketCleanupWorker do # Use the existing function from Packets context Packets.clean_old_packets() end + + @doc """ + Perform cleanup of packets older than a specific number of days. + + This function provides more granular control over packet cleanup operations. + + ## Parameters + - `days` - Number of days to retain packets (packets older than this will be deleted) + + ## Returns + - Number of packets deleted + """ + def cleanup_packets_older_than(days) when is_integer(days) and days > 0 do + Logger.info("Starting APRS packet cleanup for packets older than #{days} days") + + deleted_count = Packets.clean_packets_older_than(days) + + Logger.info("APRS packet cleanup complete: removed #{deleted_count} packets older than #{days} days") + + deleted_count + end end diff --git a/test/aprs/passcode_test.exs b/test/aprs/passcode_test.exs index 59a678b..42adad3 100644 --- a/test/aprs/passcode_test.exs +++ b/test/aprs/passcode_test.exs @@ -1,22 +1,23 @@ defmodule Aprs.PasscodeTest do use ExUnit.Case + doctest Aprs.Passcode describe "generate/1" do test "generates correct passcode for W5ISP" do - assert Aprs.Passcode.generate("W5ISP") == 15748 + assert Aprs.Passcode.generate("W5ISP") == 15_748 end test "handles callsigns with SSIDs" do - assert Aprs.Passcode.generate("W5ISP-1") == 15748 + assert Aprs.Passcode.generate("W5ISP-1") == 15_748 end test "handles lowercase callsigns" do - assert Aprs.Passcode.generate("w5isp") == 15748 + assert Aprs.Passcode.generate("w5isp") == 15_748 end test "handles long callsigns" do - assert Aprs.Passcode.generate("W5ISP-LONG") == 15748 + assert Aprs.Passcode.generate("W5ISP-LONG") == 15_748 end end end