Add deployment notification system to update timestamp in LiveViews
When a new deployment occurs in k8s, all connected LiveViews now receive a notification to update their deployment timestamp display. Changes: - Added DeploymentNotifier GenServer that broadcasts deployment events - LiveViews subscribe to "deployment_events" PubSub topic - When DEPLOYED_AT env var is set (k8s deployments), a notification is sent after app startup - LiveViews handle the deployment event and update their timestamp This ensures that when a new version is deployed, users see the deployment timestamp update in real-time without needing to refresh their browser. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
94c0f1a48c
commit
478e7b1936
4 changed files with 97 additions and 1 deletions
|
|
@ -49,6 +49,8 @@ defmodule Aprsme.Application do
|
|||
Aprsme.CleanupScheduler,
|
||||
Aprsme.Presence,
|
||||
Aprsme.PostgresNotifier,
|
||||
# Start deployment notifier
|
||||
Aprsme.DeploymentNotifier,
|
||||
# Start the packet processing pipeline
|
||||
Aprsme.PacketPipelineSupervisor
|
||||
]
|
||||
|
|
|
|||
62
lib/aprsme/deployment_notifier.ex
Normal file
62
lib/aprsme/deployment_notifier.ex
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
defmodule Aprsme.DeploymentNotifier do
|
||||
@moduledoc """
|
||||
Monitors for deployment changes and notifies connected clients.
|
||||
In k8s, this detects when the DEPLOYED_AT environment variable changes.
|
||||
"""
|
||||
use GenServer
|
||||
|
||||
require Logger
|
||||
|
||||
@check_interval :timer.seconds(30)
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
end
|
||||
|
||||
def init(_opts) do
|
||||
# Schedule first check
|
||||
Process.send_after(self(), :check_deployment, @check_interval)
|
||||
|
||||
# Get initial deployment timestamp
|
||||
deployed_at = Aprsme.Release.deployed_at()
|
||||
|
||||
{:ok, %{deployed_at: deployed_at}}
|
||||
end
|
||||
|
||||
def handle_info(:check_deployment, state) do
|
||||
# Schedule next check
|
||||
Process.send_after(self(), :check_deployment, @check_interval)
|
||||
|
||||
# Get current deployment timestamp
|
||||
current_deployed_at = Aprsme.Release.deployed_at()
|
||||
|
||||
# Check if deployment timestamp changed (shouldn't happen in same process, but useful for monitoring)
|
||||
if current_deployed_at == state.deployed_at do
|
||||
{:noreply, state}
|
||||
|
||||
# Broadcast the new deployment
|
||||
else
|
||||
Logger.info("Deployment timestamp changed from #{state.deployed_at} to #{current_deployed_at}")
|
||||
|
||||
Phoenix.PubSub.broadcast(
|
||||
Aprsme.PubSub,
|
||||
"deployment_events",
|
||||
{:new_deployment, %{deployed_at: current_deployed_at}}
|
||||
)
|
||||
|
||||
{:noreply, %{state | deployed_at: current_deployed_at}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Notify about a new deployment immediately.
|
||||
This can be called from the release module when deployment is detected.
|
||||
"""
|
||||
def notify_deployment(deployed_at) do
|
||||
Phoenix.PubSub.broadcast(
|
||||
Aprsme.PubSub,
|
||||
"deployment_events",
|
||||
{:new_deployment, %{deployed_at: deployed_at}}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -100,6 +100,27 @@ defmodule Aprsme.Release do
|
|||
# Store in application config
|
||||
Application.put_env(:aprsme, :deployed_at, deployed_at)
|
||||
|
||||
# Notify about deployment after a short delay to ensure PubSub is started
|
||||
# In k8s, this will notify all connected clients about the new deployment
|
||||
if System.get_env("DEPLOYED_AT") do
|
||||
spawn(fn ->
|
||||
# Wait for application to start
|
||||
Process.sleep(10_000)
|
||||
|
||||
try do
|
||||
require Logger
|
||||
|
||||
Aprsme.DeploymentNotifier.notify_deployment(deployed_at)
|
||||
Logger.info("Deployment notification sent for timestamp: #{deployed_at}")
|
||||
rescue
|
||||
error ->
|
||||
require Logger
|
||||
|
||||
Logger.warning("Failed to send deployment notification: #{inspect(error)}")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
deployed_at
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
alias AprsmeWeb.MapLive.UrlParams
|
||||
alias AprsmeWeb.TimeUtils
|
||||
alias Phoenix.LiveView.Socket
|
||||
alias Phoenix.Socket.Broadcast
|
||||
|
||||
@impl true
|
||||
def mount(params, session, socket) do
|
||||
|
|
@ -107,6 +108,9 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
# Still subscribe to bad packets (they don't have location)
|
||||
Phoenix.PubSub.subscribe(Aprsme.PubSub, "bad_packets")
|
||||
|
||||
# Subscribe to deployment events
|
||||
Phoenix.PubSub.subscribe(Aprsme.PubSub, "deployment_events")
|
||||
|
||||
# Subscribe to StreamingPacketsPubSub with initial bounds
|
||||
Aprsme.StreamingPacketsPubSub.subscribe_to_bounds(self(), default_bounds)
|
||||
|
||||
|
|
@ -671,6 +675,13 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
|
||||
def handle_info({:streaming_packet, packet}, socket), do: handle_info_postgres_packet(packet, socket)
|
||||
|
||||
def handle_info(
|
||||
%Broadcast{topic: "deployment_events", payload: {:new_deployment, %{deployed_at: deployed_at}}},
|
||||
socket
|
||||
) do
|
||||
{:noreply, assign(socket, :deployed_at, deployed_at)}
|
||||
end
|
||||
|
||||
def handle_info({:load_rf_path_station_packets, stations}, socket) do
|
||||
# Load the most recent packet for each RF path station
|
||||
station_packets =
|
||||
|
|
@ -722,7 +733,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_info(%Phoenix.Socket.Broadcast{topic: "aprs_messages", event: "packet", payload: packet}, socket),
|
||||
def handle_info(%Broadcast{topic: "aprs_messages", event: "packet", payload: packet}, socket),
|
||||
do: handle_info({:postgres_packet, packet}, socket)
|
||||
|
||||
def handle_info({:show_error, message}, socket) do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue