Add PromEx Prometheus exporter and /metrics endpoint
Wire up prom_ex with built-in Application, Beam, Phoenix, PhoenixLiveView,
and Ecto plugins, plus a custom plugin that exposes the existing
app-specific telemetry (packet pipeline, spatial PubSub, repo pool,
postgres stats, insert optimizer) as Prometheus metrics.
Mount PromEx.Plug at /metrics — internal-only, intended to be scraped
by the cluster's external Prometheus through the kube-apiserver pod
proxy. K8s pod template now carries prometheus.io/{scrape,port,path}
annotations so the existing kubernetes-pods scrape job picks it up
automatically.
PromEx is disabled in the test environment.
Also harden the packet cleanup and partition manager tests with
Repo.delete_all setup hooks so they aren't poisoned by residual rows
committed outside a sandbox transaction in earlier runs (which were
inflating delete counts and triggering ATTACH PARTITION check_violation
errors against packets_default).
This commit is contained in:
parent
72cd6d953b
commit
51e068f3f1
12 changed files with 434 additions and 0 deletions
|
|
@ -16,6 +16,13 @@ import Config
|
|||
# at the `config/runtime.exs`.
|
||||
config :aprsme, Aprsme.Mailer, adapter: Swoosh.Adapters.Local
|
||||
|
||||
# PromEx — Prometheus metrics exporter. Disabled in test by config/test.exs.
|
||||
config :aprsme, Aprsme.PromEx,
|
||||
manual_metrics_start_delay: :no_delay,
|
||||
drop_metrics_groups: [],
|
||||
grafana: :disabled,
|
||||
metrics_server: :disabled
|
||||
|
||||
# Configures the endpoint
|
||||
config :aprsme, AprsmeWeb.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ pool_size =
|
|||
# In test we don't send emails.
|
||||
config :aprsme, Aprsme.Mailer, adapter: Swoosh.Adapters.Test
|
||||
|
||||
# Disable PromEx in test environment
|
||||
config :aprsme, Aprsme.PromEx, disabled: true
|
||||
|
||||
config :aprsme, Aprsme.Repo,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ spec:
|
|||
metadata:
|
||||
labels:
|
||||
app: aprs
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "4000"
|
||||
prometheus.io/path: "/metrics"
|
||||
spec:
|
||||
priorityClassName: system-cluster-critical
|
||||
terminationGracePeriodSeconds: 90
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ defmodule Aprsme.Application do
|
|||
children = [
|
||||
# Start the Telemetry supervisor
|
||||
AprsmeWeb.Telemetry,
|
||||
# Start the Prometheus metrics exporter
|
||||
Aprsme.PromEx,
|
||||
# Start the Ecto repository
|
||||
Aprsme.Repo,
|
||||
# Start the PubSub system
|
||||
|
|
|
|||
42
lib/aprsme/prom_ex.ex
Normal file
42
lib/aprsme/prom_ex.ex
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
defmodule Aprsme.PromEx do
|
||||
@moduledoc """
|
||||
PromEx supervision module for the aprs.me application.
|
||||
|
||||
Built-in plugins provide metrics for Phoenix, Phoenix LiveView, Ecto, the BEAM
|
||||
VM, and the Application/release. The custom `Aprsme.PromEx.Plugins.Aprsme`
|
||||
plugin exposes the app-specific telemetry events emitted by the GenStage
|
||||
packet pipeline, SpatialPubSub, and the periodic Postgres collectors.
|
||||
|
||||
Metrics are scraped at `GET /metrics` on the Phoenix endpoint via
|
||||
`PromEx.Plug` (mounted in the router).
|
||||
"""
|
||||
|
||||
use PromEx, otp_app: :aprsme
|
||||
|
||||
alias PromEx.Plugins
|
||||
|
||||
@impl true
|
||||
def plugins do
|
||||
[
|
||||
Plugins.Application,
|
||||
Plugins.Beam,
|
||||
{Plugins.Phoenix, router: AprsmeWeb.Router, endpoint: AprsmeWeb.Endpoint},
|
||||
Plugins.PhoenixLiveView,
|
||||
{Plugins.Ecto, otp_app: :aprsme, repos: [Aprsme.Repo]},
|
||||
Aprsme.PromEx.Plugins.Aprsme
|
||||
]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def dashboard_assigns do
|
||||
[
|
||||
datasource_id: "Prometheus",
|
||||
default_selected_interval: "30s"
|
||||
]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def dashboards do
|
||||
[]
|
||||
end
|
||||
end
|
||||
334
lib/aprsme/prom_ex/plugins/aprsme.ex
Normal file
334
lib/aprsme/prom_ex/plugins/aprsme.ex
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
defmodule Aprsme.PromEx.Plugins.Aprsme do
|
||||
@moduledoc """
|
||||
PromEx plugin exposing app-specific telemetry as Prometheus metrics.
|
||||
|
||||
Mirrors the metrics already defined for LiveDashboard in
|
||||
`AprsmeWeb.Telemetry.metrics/0`, grouping them by subsystem:
|
||||
|
||||
* GenStage packet pipeline (`[:aprsme, :packet_pipeline, :batch]`)
|
||||
* Producer backpressure (`[:aprsme, :packet_producer, :backpressure]`)
|
||||
* Spatial PubSub (`[:aprsme, :spatial_pubsub, ...]`)
|
||||
* Database connection pool (`[:aprsme, :repo, :pool]`)
|
||||
* Postgres database stats (`[:aprsme, :postgres, ...]`)
|
||||
* Insert optimizer (`[:aprsme, :insert_optimizer, ...]`)
|
||||
"""
|
||||
|
||||
use PromEx.Plugin
|
||||
|
||||
@duration_buckets [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10_000]
|
||||
|
||||
@impl true
|
||||
def event_metrics(_opts) do
|
||||
[
|
||||
packet_pipeline_metrics(),
|
||||
producer_backpressure_metrics(),
|
||||
spatial_pubsub_metrics(),
|
||||
repo_pool_metrics(),
|
||||
postgres_metrics(),
|
||||
insert_optimizer_metrics()
|
||||
]
|
||||
end
|
||||
|
||||
defp packet_pipeline_metrics do
|
||||
Event.build(
|
||||
:aprsme_packet_pipeline_event_metrics,
|
||||
[
|
||||
counter(
|
||||
[:aprsme, :packet_pipeline, :batch, :count],
|
||||
event_name: [:aprsme, :packet_pipeline, :batch],
|
||||
measurement: :count,
|
||||
description: "Total number of packets processed in batches"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :packet_pipeline, :batch, :success],
|
||||
event_name: [:aprsme, :packet_pipeline, :batch],
|
||||
measurement: :success,
|
||||
description: "Total number of successful inserts"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :packet_pipeline, :batch, :error],
|
||||
event_name: [:aprsme, :packet_pipeline, :batch],
|
||||
measurement: :error,
|
||||
description: "Total number of insert errors"
|
||||
),
|
||||
distribution(
|
||||
[:aprsme, :packet_pipeline, :batch, :duration_milliseconds],
|
||||
event_name: [:aprsme, :packet_pipeline, :batch],
|
||||
measurement: :duration_ms,
|
||||
description: "Batch insert duration",
|
||||
unit: :millisecond,
|
||||
reporter_options: [buckets: @duration_buckets]
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp producer_backpressure_metrics do
|
||||
Event.build(
|
||||
:aprsme_packet_producer_event_metrics,
|
||||
[
|
||||
counter(
|
||||
[:aprsme, :packet_producer, :backpressure, :count],
|
||||
event_name: [:aprsme, :packet_producer, :backpressure],
|
||||
measurement: :count,
|
||||
description: "Backpressure state changes in the packet producer"
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp spatial_pubsub_metrics do
|
||||
Event.build(
|
||||
:aprsme_spatial_pubsub_event_metrics,
|
||||
[
|
||||
last_value(
|
||||
[:aprsme, :spatial_pubsub, :clients, :count],
|
||||
event_name: [:aprsme, :spatial_pubsub, :clients],
|
||||
measurement: :count,
|
||||
description: "Number of connected spatial PubSub clients"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :spatial_pubsub, :clients, :grid_cells],
|
||||
event_name: [:aprsme, :spatial_pubsub, :clients],
|
||||
measurement: :grid_cells,
|
||||
description: "Number of active grid cells"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :spatial_pubsub, :clients, :avg_clients_per_cell],
|
||||
event_name: [:aprsme, :spatial_pubsub, :clients],
|
||||
measurement: :avg_clients_per_cell,
|
||||
description: "Average clients per grid cell"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :spatial_pubsub, :broadcasts, :total],
|
||||
event_name: [:aprsme, :spatial_pubsub, :broadcasts],
|
||||
measurement: :total,
|
||||
description: "Total spatial broadcasts sent"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :spatial_pubsub, :broadcasts, :filtered],
|
||||
event_name: [:aprsme, :spatial_pubsub, :broadcasts],
|
||||
measurement: :filtered,
|
||||
description: "Broadcasts filtered out by viewport"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :spatial_pubsub, :broadcasts, :packets],
|
||||
event_name: [:aprsme, :spatial_pubsub, :broadcasts],
|
||||
measurement: :packets,
|
||||
description: "Total packets processed by spatial PubSub"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :spatial_pubsub, :efficiency, :ratio],
|
||||
event_name: [:aprsme, :spatial_pubsub, :efficiency],
|
||||
measurement: :ratio,
|
||||
description: "Broadcast efficiency ratio (0-1)"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :spatial_pubsub, :efficiency, :saved_broadcasts],
|
||||
event_name: [:aprsme, :spatial_pubsub, :efficiency],
|
||||
measurement: :saved_broadcasts,
|
||||
description: "Number of broadcasts saved by viewport filtering"
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp repo_pool_metrics do
|
||||
Event.build(
|
||||
:aprsme_repo_pool_event_metrics,
|
||||
[
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :size],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :size,
|
||||
description: "Configured DB connection pool size"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :idle],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :idle,
|
||||
description: "Idle DB connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :busy],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :busy,
|
||||
description: "Busy DB connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :available],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :available,
|
||||
description: "Available DB connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :queue_length],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :queue_length,
|
||||
description: "Processes waiting on a DB connection"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :repo, :pool, :total],
|
||||
event_name: [:aprsme, :repo, :pool],
|
||||
measurement: :total,
|
||||
description: "Total connections in the DB pool"
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp postgres_metrics do
|
||||
Event.build(
|
||||
:aprsme_postgres_event_metrics,
|
||||
[
|
||||
last_value(
|
||||
[:aprsme, :postgres, :database, :size_bytes],
|
||||
event_name: [:aprsme, :postgres, :database],
|
||||
measurement: :size_bytes,
|
||||
description: "Total Postgres database size in bytes",
|
||||
unit: :byte
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :connections, :total],
|
||||
event_name: [:aprsme, :postgres, :connections],
|
||||
measurement: :total,
|
||||
description: "Total Postgres connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :connections, :active],
|
||||
event_name: [:aprsme, :postgres, :connections],
|
||||
measurement: :active,
|
||||
description: "Active Postgres connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :connections, :idle],
|
||||
event_name: [:aprsme, :postgres, :connections],
|
||||
measurement: :idle,
|
||||
description: "Idle Postgres connections"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :connections, :idle_in_transaction],
|
||||
event_name: [:aprsme, :postgres, :connections],
|
||||
measurement: :idle_in_transaction,
|
||||
description: "Connections idle in transaction"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :connections, :waiting],
|
||||
event_name: [:aprsme, :postgres, :connections],
|
||||
measurement: :waiting,
|
||||
description: "Postgres connections waiting on locks"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :packets_table, :live_tuples],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :live_tuples,
|
||||
description: "Live rows in the packets table"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :packets_table, :dead_tuples],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :dead_tuples,
|
||||
description: "Dead rows in the packets table"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :postgres, :packets_table, :total_inserts],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :total_inserts,
|
||||
description: "Total inserts into the packets table"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :postgres, :packets_table, :total_updates],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :total_updates,
|
||||
description: "Total updates on the packets table"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :postgres, :packets_table, :total_deletes],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :total_deletes,
|
||||
description: "Total deletes from the packets table"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :packets_table, :table_size_bytes],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :table_size_bytes,
|
||||
description: "Packets table size in bytes",
|
||||
unit: :byte
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :packets_table, :indexes_size_bytes],
|
||||
event_name: [:aprsme, :postgres, :packets_table],
|
||||
measurement: :indexes_size_bytes,
|
||||
description: "Packets indexes size in bytes",
|
||||
unit: :byte
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :postgres, :query_stats, :total_calls],
|
||||
event_name: [:aprsme, :postgres, :query_stats],
|
||||
measurement: :total_calls,
|
||||
description: "Total Postgres queries executed (pg_stat_statements)"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :query_stats, :total_time_ms],
|
||||
event_name: [:aprsme, :postgres, :query_stats],
|
||||
measurement: :total_time_ms,
|
||||
description: "Total Postgres query execution time",
|
||||
unit: :millisecond
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :query_stats, :avg_time_ms],
|
||||
event_name: [:aprsme, :postgres, :query_stats],
|
||||
measurement: :avg_time_ms,
|
||||
description: "Average Postgres query execution time",
|
||||
unit: :millisecond
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :query_stats, :max_time_ms],
|
||||
event_name: [:aprsme, :postgres, :query_stats],
|
||||
measurement: :max_time_ms,
|
||||
description: "Maximum Postgres query execution time",
|
||||
unit: :millisecond
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :postgres, :replication, :lag_seconds],
|
||||
event_name: [:aprsme, :postgres, :replication],
|
||||
measurement: :lag_seconds,
|
||||
description: "Postgres replication lag in seconds",
|
||||
unit: :second
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
defp insert_optimizer_metrics do
|
||||
Event.build(
|
||||
:aprsme_insert_optimizer_event_metrics,
|
||||
[
|
||||
last_value(
|
||||
[:aprsme, :insert_optimizer, :batch_size],
|
||||
event_name: [:aprsme, :insert_optimizer, :batch_size],
|
||||
measurement: :value,
|
||||
description: "Current batch size chosen by the insert optimizer"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :insert_optimizer, :throughput],
|
||||
event_name: [:aprsme, :insert_optimizer, :throughput],
|
||||
measurement: :value,
|
||||
description: "Average insert throughput observed by the optimizer"
|
||||
),
|
||||
last_value(
|
||||
[:aprsme, :insert_optimizer, :duration],
|
||||
event_name: [:aprsme, :insert_optimizer, :duration],
|
||||
measurement: :value,
|
||||
description: "Average insert duration observed by the optimizer"
|
||||
),
|
||||
counter(
|
||||
[:aprsme, :insert_optimizer, :optimizations],
|
||||
event_name: [:aprsme, :insert_optimizer, :optimizations],
|
||||
measurement: :count,
|
||||
description: "Number of times the optimizer adjusted batch parameters"
|
||||
)
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -72,6 +72,10 @@ defmodule AprsmeWeb.Router do
|
|||
get "/status.json", PageController, :status_json
|
||||
end
|
||||
|
||||
# Prometheus metrics endpoint — internal-only, scraped by the cluster's
|
||||
# prometheus via the kube-apiserver pod proxy. Not exposed via Ingress.
|
||||
forward "/metrics", PromEx.Plug, prom_ex_module: Aprsme.PromEx
|
||||
|
||||
# Agent/crawler discovery endpoints
|
||||
scope "/", AprsmeWeb do
|
||||
get "/sitemap.xml", PageController, :sitemap
|
||||
|
|
|
|||
1
mix.exs
1
mix.exs
|
|
@ -98,6 +98,7 @@ defmodule Aprsme.MixProject do
|
|||
{:swoosh, "~> 1.23"},
|
||||
{:telemetry_metrics, "~> 1.0"},
|
||||
{:telemetry_poller, "~> 1.0"},
|
||||
{:prom_ex, "~> 1.11"},
|
||||
aprs_dep(),
|
||||
{:esbuild, "~> 0.10", runtime: Mix.env() == :dev},
|
||||
{:tailwind, "~> 0.4.0", runtime: Mix.env() == :dev},
|
||||
|
|
|
|||
6
mix.lock
6
mix.lock
|
|
@ -3,6 +3,7 @@
|
|||
"bandit": {:hex, :bandit, "1.11.0", "dbdd9c9963f146ee9da9860d1ee5b0ffd65cea51fe2aab3f3273df84329d133a", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "c949d93a325a28da2333dde5a9ab61986ad2c2b7226347db6a28303b9139865e"},
|
||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.3.2", "d50091e3c9492d73e17fc1e1619a9b09d6a5ef99160eb4d736926fd475a16ca3", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "471be5151874ae7931911057d1467d908955f93554f7a6cd1b7d804cac8cef53"},
|
||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||
"castore": {:hex, :castore, "1.0.18", "5e43ef0ec7d31195dfa5a65a86e6131db999d074179d2ba5a8de11fe14570f55", [:mix], [], "hexpm", "f393e4fe6317829b158fb74d86eb681f737d2fe326aa61ccf6293c4104957e34"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
||||
"circular_buffer": {:hex, :circular_buffer, "1.0.0", "25c004da0cba7bd8bc1bdabded4f9a902d095e20600fd15faf1f2ffbaea18a07", [:mix], [], "hexpm", "c829ec31c13c7bafd1f546677263dff5bfb006e929f25635878ac3cfba8749e5"},
|
||||
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
|
||||
|
|
@ -37,6 +38,8 @@
|
|||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||
"nimble_ownership": {:hex, :nimble_ownership, "1.0.2", "fa8a6f2d8c592ad4d79b2ca617473c6aefd5869abfa02563a77682038bf916cf", [:mix], [], "hexpm", "098af64e1f6f8609c6672127cfe9e9590a5d3fcdd82bc17a377b8692fd81a879"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||
"octo_fetch": {:hex, :octo_fetch, "0.5.0", "f50701568b9fc752656367f82cc134d5fbefff37c5a0e8ddfcceb02ceee3f5fc", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6226cc3c14ca948ee9f25fb0446322e5c288e215da9beba7899b6b5f4cd3ccb0"},
|
||||
"peep": {:hex, :peep, "3.5.0", "9f6ead7b0f2c684494200c8fc02e7e62e8c459afe861b29bd859e4c96f402ed8", [:mix], [{:nimble_options, "~> 1.1", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.16", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5a73a99c6e60062415efeb7e536a663387146463a3d3df1417da31fd665ac210"},
|
||||
"phoenix": {:hex, :phoenix, "1.8.6", "7106a0da114619c4b12b056bbaef39fdbc75d3d0cf9cf24af683364064c12dc3", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "d0d0b7931916c8196b6903a1efa118b5da28487e7a75ad32a54dfd77de59d421"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"},
|
||||
|
|
@ -48,13 +51,16 @@
|
|||
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
||||
"postgrex": {:hex, :postgrex, "0.22.1", "b3665ad17e15441557da8f45eeebfcd56e4a2b0b98538b855679a13d05e5cc5d", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "df59f828b167b49a5853f645b65f57eb1bc5f3b230497ceaca7af5d8ac05afef"},
|
||||
"prom_ex": {:hex, :prom_ex, "1.11.0", "1f6d67f2dead92224cb4f59beb3e4d319257c5728d9638b4a5e8ceb51a4f9c7e", [:mix], [{:absinthe, ">= 1.7.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.1.0", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.11.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.18", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.10.0", [hex: :oban, repo: "hexpm", optional: true]}, {:octo_fetch, "~> 0.4", [hex: :octo_fetch, repo: "hexpm", optional: false]}, {:peep, "~> 3.0", [hex: :peep, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.7.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.20.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.16.0", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 2.6.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, ">= 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.2", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.1", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "76b074bc3730f0802978a7eb5c7091a65473eaaf07e99ec9e933138dcc327805"},
|
||||
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
||||
"stream_data": {:hex, :stream_data, "1.3.0", "bde37905530aff386dea1ddd86ecbf00e6642dc074ceffc10b7d4e41dfd6aac9", [:mix], [], "hexpm", "3cc552e286e817dca43c98044c706eec9318083a1480c52ae2688b08e2936e3c"},
|
||||
"styler": {:hex, :styler, "1.11.0", "35010d970689a23c2bcc8e97bd8bf7d20e3561d60c49be84654df5c37d051a9c", [:mix], [], "hexpm", "70f36165d0cf238a32b7a456fdef6a9c72e77e657d7ac4a0ace33aeba3f2b8c0"},
|
||||
"swoosh": {:hex, :swoosh, "1.25.1", "569fcff34817da8a03f28775146b3c8b71b4c9b14f8f78d37ff3ef422862a18b", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "58b3e8db6406fe417a89b5042358d2e8f15d32a3317d4f8581d7a3ae501e410b"},
|
||||
"tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"},
|
||||
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
|
||||
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
|
||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
||||
"tidewave": {:hex, :tidewave, "0.5.6", "91f35540b5599640443f1d3a1c6166bf506e202840261a6344e384e8813c1f64", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_reload, ">= 1.6.1", [hex: :phoenix_live_reload, repo: "hexpm", optional: true]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "dc82d52b8b6ffc04680544b17cd340c7d4166bb0d63999eb960850526866b533"},
|
||||
|
|
|
|||
|
|
@ -997,6 +997,14 @@ defmodule Aprsme.PacketsTest do
|
|||
end
|
||||
|
||||
describe "clean_old_packets/0" do
|
||||
setup do
|
||||
# Defensive: ensure a clean slate so residual rows from prior runs
|
||||
# (e.g. data committed outside a sandbox transaction) don't inflate
|
||||
# the deletion count.
|
||||
Repo.delete_all(Packet)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "deletes packets older than retention period" do
|
||||
now = DateTime.utc_now()
|
||||
|
||||
|
|
@ -1024,6 +1032,11 @@ defmodule Aprsme.PacketsTest do
|
|||
end
|
||||
|
||||
describe "clean_packets_older_than/1" do
|
||||
setup do
|
||||
Repo.delete_all(Packet)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "deletes packets older than specified days" do
|
||||
now = DateTime.utc_now()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
defmodule Aprsme.PartitionManagerTest do
|
||||
use Aprsme.DataCase, async: false
|
||||
|
||||
alias Aprsme.Packet
|
||||
alias Aprsme.PartitionManager
|
||||
|
||||
setup do
|
||||
# Defensive: any rows in packets_default block ATTACH PARTITION on
|
||||
# overlapping ranges with a check_violation. Wipe the parent table
|
||||
# so partition tests can freely create/drop partitions.
|
||||
Repo.delete_all(Packet)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "partition_name/1" do
|
||||
test "generates correct name for a date" do
|
||||
assert PartitionManager.partition_name(~D[2026-02-20]) == "packets_20260220"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@ defmodule Aprsme.Workers.PacketCleanupWorkerTest do
|
|||
use Aprsme.DataCase, async: false
|
||||
|
||||
alias Aprsme.BadPacket
|
||||
alias Aprsme.Packet
|
||||
alias Aprsme.PartitionManager
|
||||
alias Aprsme.Workers.PacketCleanupWorker
|
||||
|
||||
setup do
|
||||
# Defensive: rows in packets_default whose received_at falls inside a
|
||||
# range we're about to ATTACH would trigger a check_violation. Wipe
|
||||
# the parent so partition operations are unobstructed.
|
||||
Repo.delete_all(Packet)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "perform/1 with cleanup_days" do
|
||||
test "drops old partitions via PartitionManager" do
|
||||
# Ensure current partitions exist
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue