Make leader election wait for cluster formation
- Leader election now waits for other nodes to connect before starting - Added cleanup of stale global registrations from disconnected nodes - Only starts election after cluster is established in clustered mode - Non-clustered mode continues to elect immediately This prevents both nodes from becoming leaders when they start before the cluster is fully formed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9212088beb
commit
9caab57dbe
1 changed files with 51 additions and 3 deletions
|
|
@ -26,17 +26,46 @@ defmodule Aprsme.Cluster.LeaderElection do
|
||||||
def init(_opts) do
|
def init(_opts) do
|
||||||
Logger.info("Starting leader election process")
|
Logger.info("Starting leader election process")
|
||||||
|
|
||||||
# Schedule initial election
|
cluster_enabled = Application.get_env(:aprsme, :cluster_enabled, false)
|
||||||
Process.send_after(self(), :attempt_election, 100)
|
|
||||||
|
if cluster_enabled do
|
||||||
|
Logger.info("Clustering enabled - waiting for cluster formation before leader election")
|
||||||
|
# Wait longer for cluster to form, then check periodically
|
||||||
|
Process.send_after(self(), :check_cluster_and_elect, 2_000)
|
||||||
|
else
|
||||||
|
Logger.info("Clustering disabled - proceeding with immediate leader election")
|
||||||
|
# Non-clustered mode - elect immediately
|
||||||
|
Process.send_after(self(), :attempt_election, 100)
|
||||||
|
end
|
||||||
|
|
||||||
# Schedule periodic checks
|
# Schedule periodic checks
|
||||||
Process.send_after(self(), :check_leadership, @check_interval)
|
Process.send_after(self(), :check_leadership, @check_interval)
|
||||||
|
|
||||||
{:ok, %{is_leader: false, leader_node: nil}}
|
{:ok, %{is_leader: false, leader_node: nil, cluster_enabled: cluster_enabled}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info(:check_cluster_and_elect, state) do
|
||||||
|
connected_nodes = Node.list()
|
||||||
|
|
||||||
|
if length(connected_nodes) > 0 do
|
||||||
|
Logger.info("Cluster formed with #{length(connected_nodes)} other nodes: #{inspect(connected_nodes)}")
|
||||||
|
Logger.info("Proceeding with leader election")
|
||||||
|
Process.send_after(self(), :attempt_election, 100)
|
||||||
|
{:noreply, state}
|
||||||
|
else
|
||||||
|
Logger.info("Cluster not yet formed - waiting...")
|
||||||
|
# Check again in 2 seconds
|
||||||
|
Process.send_after(self(), :check_cluster_and_elect, 2_000)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info(:attempt_election, state) do
|
def handle_info(:attempt_election, state) do
|
||||||
|
# First, try to clean up any stale registrations
|
||||||
|
cleanup_stale_registrations()
|
||||||
|
|
||||||
case :global.register_name(@election_key, self(), &resolve_conflict/3) do
|
case :global.register_name(@election_key, self(), &resolve_conflict/3) do
|
||||||
:yes ->
|
:yes ->
|
||||||
Logger.info("Elected as APRS-IS connection leader on node #{node()}")
|
Logger.info("Elected as APRS-IS connection leader on node #{node()}")
|
||||||
|
|
@ -100,6 +129,25 @@ defmodule Aprsme.Cluster.LeaderElection do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp cleanup_stale_registrations do
|
||||||
|
case :global.whereis_name(@election_key) do
|
||||||
|
:undefined ->
|
||||||
|
# No registration exists
|
||||||
|
:ok
|
||||||
|
|
||||||
|
pid when is_pid(pid) ->
|
||||||
|
# Check if the registered process is alive and on a connected node
|
||||||
|
if Process.alive?(pid) and node(pid) in [node() | Node.list()] do
|
||||||
|
# Process is alive and on a connected node - leave it alone
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
# Process is dead or on a disconnected node - unregister it
|
||||||
|
Logger.info("Cleaning up stale leader registration for dead/disconnected process #{inspect(pid)}")
|
||||||
|
:global.unregister_name(@election_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp notify_leadership_change(became_leader) do
|
defp notify_leadership_change(became_leader) do
|
||||||
Phoenix.PubSub.broadcast(
|
Phoenix.PubSub.broadcast(
|
||||||
Aprsme.PubSub,
|
Aprsme.PubSub,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue