diff --git a/lib/aprsme/cluster/leader_election.ex b/lib/aprsme/cluster/leader_election.ex index 3c9aa0d..0736a47 100644 --- a/lib/aprsme/cluster/leader_election.ex +++ b/lib/aprsme/cluster/leader_election.ex @@ -8,9 +8,16 @@ defmodule Aprsme.Cluster.LeaderElection do require Logger @election_key {:aprs_is_leader, __MODULE__} - @check_interval 5_000 + @default_check_interval 5_000 # Maximum time to wait for cluster formation before proceeding with election (30 seconds) - @max_cluster_wait 30_000 + @default_max_cluster_wait 30_000 + @default_cluster_check_interval 2_000 + + defp check_interval, do: Application.get_env(:aprsme, :election_check_interval_ms, @default_check_interval) + defp max_cluster_wait, do: Application.get_env(:aprsme, :election_max_cluster_wait_ms, @default_max_cluster_wait) + + defp cluster_check_interval, + do: Application.get_env(:aprsme, :election_cluster_check_interval_ms, @default_cluster_check_interval) defstruct is_leader: false, leader_node: nil, @@ -63,7 +70,7 @@ defmodule Aprsme.Cluster.LeaderElection do schedule_initial_election(cluster_enabled) # Schedule periodic checks - Process.send_after(self(), :check_leadership, @check_interval) + Process.send_after(self(), :check_leadership, check_interval()) # Initialize cached leadership state :persistent_term.put({__MODULE__, :is_leader}, false) @@ -74,8 +81,8 @@ defmodule Aprsme.Cluster.LeaderElection do # Clustered mode: wait for formation, then elect; schedule a backstop timeout. defp schedule_initial_election(true) do Logger.info("Clustering enabled - waiting for cluster formation before leader election") - Process.send_after(self(), :check_cluster_and_elect, 2_000) - Process.send_after(self(), :force_election_timeout, @max_cluster_wait) + Process.send_after(self(), :check_cluster_and_elect, cluster_check_interval()) + Process.send_after(self(), :force_election_timeout, max_cluster_wait()) end # Non-clustered mode: elect immediately. @@ -118,12 +125,12 @@ defmodule Aprsme.Cluster.LeaderElection do if connected_nodes == [] do Logger.warning( - "Cluster formation timeout reached after #{@max_cluster_wait}ms with no connected nodes. " <> + "Cluster formation timeout reached after #{max_cluster_wait()}ms with no connected nodes. " <> "Proceeding with leader election in single-node mode to ensure APRS-IS connection." ) else Logger.info( - "Forcing leader election after #{@max_cluster_wait}ms wait with #{length(connected_nodes)} connected nodes" + "Forcing leader election after #{max_cluster_wait()}ms wait with #{length(connected_nodes)} connected nodes" ) end @@ -165,7 +172,7 @@ defmodule Aprsme.Cluster.LeaderElection do end # Schedule next check - _ = Process.send_after(self(), :check_leadership, @check_interval) + _ = Process.send_after(self(), :check_leadership, check_interval()) {:noreply, state} end diff --git a/test/aprsme/cluster/leader_election_test.exs b/test/aprsme/cluster/leader_election_test.exs index a4aed70..b51ec56 100644 --- a/test/aprsme/cluster/leader_election_test.exs +++ b/test/aprsme/cluster/leader_election_test.exs @@ -161,15 +161,17 @@ defmodule Aprsme.Cluster.LeaderElectionTest do assert :global.whereis_name(@election_key) == :undefined end - @tag :slow test "periodic leadership check continues running" do + Application.put_env(:aprsme, :election_check_interval_ms, 50) + on_exit(fn -> Application.delete_env(:aprsme, :election_check_interval_ms) end) + {:ok, _pid} = LeaderElection.start_link([]) # Initial state - might not be leader yet _initial_leader = LeaderElection.leader?() - # Wait for periodic check - Process.sleep(6000) + # Wait for periodic check (50ms interval, plus buffer) + Process.sleep(150) # Should still be able to query leadership current_leader = LeaderElection.leader?() @@ -208,12 +210,11 @@ defmodule Aprsme.Cluster.LeaderElectionTest do end describe "stale registration cleanup" do - @tag :slow test "cleans up registration when process dies" do # Register a dead process dead_pid = spawn(fn -> :ok end) # Ensure it's dead - Process.sleep(50) + Process.sleep(10) # Force register it globally (simulating stale registration) :global.re_register_name(@election_key, dead_pid) @@ -222,8 +223,8 @@ defmodule Aprsme.Cluster.LeaderElectionTest do Application.put_env(:aprsme, :cluster_enabled, false) {:ok, _pid} = LeaderElection.start_link([]) - # Wait longer for cleanup and election to occur - Process.sleep(500) + # Wait for cleanup and election to occur + Process.sleep(150) # Should have taken over leadership assert LeaderElection.leader?() == true @@ -236,16 +237,20 @@ defmodule Aprsme.Cluster.LeaderElectionTest do :ok end - @tag :slow test "waits for cluster formation when enabled" do + Application.put_env(:aprsme, :election_max_cluster_wait_ms, 100) + Application.put_env(:aprsme, :election_cluster_check_interval_ms, 50) + + on_exit(fn -> + Application.delete_env(:aprsme, :election_max_cluster_wait_ms) + Application.delete_env(:aprsme, :election_cluster_check_interval_ms) + end) + {:ok, _pid} = LeaderElection.start_link([]) - # Immediately after start, might not be leader yet - Process.sleep(100) - # In cluster mode with no other nodes, it will eventually become leader - # but might take longer than non-clustered mode - Process.sleep(3000) + # after the (now shortened) max_cluster_wait timeout fires + Process.sleep(250) # Should eventually attempt election assert is_boolean(LeaderElection.leader?()) diff --git a/test/mix/tasks/compile/unused_test.exs b/test/mix/tasks/compile/unused_test.exs index 4993045..c13e3ed 100644 --- a/test/mix/tasks/compile/unused_test.exs +++ b/test/mix/tasks/compile/unused_test.exs @@ -22,7 +22,6 @@ defmodule Mix.Tasks.Compile.UnusedTest do assert Unused.clean() == :ok end - @tag :slow test "runs against the project and returns either :ok or :error tuple" do ExUnit.CaptureIO.capture_io(fn -> result = Unused.run([]) @@ -37,7 +36,6 @@ defmodule Mix.Tasks.Compile.UnusedTest do end) end - @tag :slow test "accepts --severity warning, info, and unknown values" do for severity <- ["warning", "info", "garbage"] do ExUnit.CaptureIO.capture_io(fn -> diff --git a/test/test_helper.exs b/test/test_helper.exs index 7cf3d8c..fa49c27 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -10,7 +10,7 @@ ExUnit.start( max_cases: max_cases, timeout: 30_000, capture_log: true, - exclude: [:slow, :integration] + exclude: [:integration] ) Ecto.Adapters.SQL.Sandbox.mode(Aprsme.Repo, :manual)