defmodule Integrations.Postgres.Display do @moduledoc """ Dashboard panel for `Integrations.Postgres` (same package). Nested under the monitor's own namespace deliberately — the package's top-level module name never changes, and this module's name is guaranteed distinct from any separately-published standalone display package. `Display.BundledDefault` auto-hooks this whenever `Integrations.Postgres` starts, the same as if they were still one module. """ use CodeNameRaven.Display, category: :database, size_hint: :full @default_port 5432 @default_database "postgres" @impl true def display_name, do: "Postgres" @impl true def compatible_monitors, do: [Integrations.Postgres] @impl true def render(assigns) do window_seconds = assigns.config.display_window_seconds cutoff = DateTime.add(DateTime.utc_now(), -window_seconds, :second) samples = CodeNameRaven.Runtime.recent_samples(assigns.config.id, limit: 120) samples = Enum.filter(samples, &(DateTime.compare(&1.collected_at, cutoff) != :lt)) local_id = CodeNameRaven.Runtime.instance_id() result = assigns[:result] role = result && result.role_metrics db = result && result.db_metrics bgw = result && result.bgwriter_metrics assigns = Map.merge(assigns, %{ samples: samples, local_id: local_id, result: result, role: role, db: db, bgw: bgw }) ~H"""
<%!-- Header --%>

<%= @config.name || @config.params[:host] || "Postgres" %>

<%= subtitle(@config.params) %>

<.status_chip status={@status} /> <.role_badge :if={@result} result={@result} />
<%!-- Row 1: Vital signs --%>
<.stat label="Latency" value={"#{@result && @result.latency_ms || "–"} ms"} /> <.stat label="Connections" value={conn_label(@role)} /> <.stat label="Idle in Tx" value={"#{(@role && @role.idle_in_transaction) || "–"}"} /> <.stat label="Lock Waiters" value={"#{(@role && @role.waiting_on_lock) || "–"}"} />
<%!-- Error --%>

<%= @last_error %>

<%!-- Row 2: Performance charts --%>

Transactions / sec

Buffer Cache Hit Ratio

Tuple Throughput (fetched / sec)

Deadlocks / sec

<%!-- Row 3: Operational stats --%>
<.stat label="DB Size" value={format_bytes(@db && @db.db_size_bytes)} /> <.stat label="XID Age" value={format_xid_age(@db && @db.xid_wraparound_age)} /> <.stat label="Checkpoint Req/s" value={format_rate(@bgw && @bgw.checkpoints_req_rate)} /> <.stat label="Buffers Backend/s" value={format_rate(@bgw && @bgw.buffers_backend_rate)} />
<%!-- Row 4: Legacy latency chart --%>

Latency

<%!-- Footer --%>

Checked <%= CodeNameRaven.Timezone.format_datetime(@last_checked_at, __MODULE__) %>

""" end # --------------------------------------------------------------------------- # Private components # --------------------------------------------------------------------------- defp status_chip(%{status: :up} = assigns) do ~H""" Up """ end defp status_chip(%{status: :degraded} = assigns) do ~H""" Degraded """ end defp status_chip(%{status: :down} = assigns) do ~H""" Down """ end defp status_chip(assigns) do ~H""" Unknown """ end defp role_badge(%{result: %{is_replica: false}} = assigns) do ~H""" PRIMARY """ end defp role_badge(%{result: %{is_replica: true}} = assigns) do ~H""" REPLICA """ end defp role_badge(assigns), do: ~H"" defp stat(assigns) do ~H"""

<%= @label %>

<%= @value %>

""" end # --------------------------------------------------------------------------- # Private helpers # --------------------------------------------------------------------------- defp subtitle(params) do host = params[:host] || params["host"] || "unknown" port = params[:port] || params["port"] || @default_port db = params[:database] || params["database"] || @default_database "#{host}:#{port}/#{db}" end defp conn_label(nil), do: "–" defp conn_label(%{active_connections: a, max_connections: m}), do: "#{a}/#{m}" defp conn_label(_), do: "–" defp format_bytes(nil), do: "–" defp format_bytes(b) when b >= 1_073_741_824, do: "#{Float.round(b / 1_073_741_824, 1)} GB" defp format_bytes(b) when b >= 1_048_576, do: "#{Float.round(b / 1_048_576, 1)} MB" defp format_bytes(b) when b >= 1_024, do: "#{Float.round(b / 1_024, 1)} KB" defp format_bytes(b), do: "#{b} B" defp format_xid_age(nil), do: "–" defp format_xid_age(age) when age >= 1_000_000_000, do: "#{Float.round(age / 1.0e9, 2)}B" defp format_xid_age(age) when age >= 1_000_000, do: "#{Float.round(age / 1.0e6, 1)}M" defp format_xid_age(age), do: "#{age}" defp format_rate(nil), do: "–" defp format_rate(r) when is_float(r), do: "#{Float.round(r, 2)}/s" defp format_rate(r), do: "#{r}/s" end