Try it in five minutes (lab)¶
The fastest way to see pg-regression-radar do its job: a throwaway lab that runs in any Kubernetes cluster (kind, minikube, k3d, Docker Desktop) and needs nothing but kubectl. No Postgres of your own, no ArgoCD, no Slack, no credentials, no helm.
The story it plays out:
- A small app,
orders-api, runs the same lookup query against a Postgres database about ten times a second. The radar learns what "normal" looks like. - You ship version 2 of
orders-api. Its database migration drops an index. The code and the queries are unchanged, but every lookup now scans the whole table. - The radar notices the slowdown, ties it to that rollout, and tells you what changed in the query plan:
Index Scan -> Seq Scan.
Everything lives in the pgrr-lab namespace and is deleted with it. The manifests are in examples/lab.
sequenceDiagram
autonumber
participant You
participant K as Kubernetes
participant R as Radar
participant P as Postgres
participant S as alert-sink
loop every 5 s, about 3 minutes
R->>P: scrape pg_stat_statements
end
Note over R: baseline learned
You->>K: apply 20-bad-deploy.yaml
K-->>R: rollout finished on a new revision
R->>P: keep scraping
R->>R: compare latency before and after
R->>K: create PerformanceRegression
R->>S: alert with the plan diff
The lab in time order: learn a baseline, ship the bad deploy, and the radar ties the slowdown to that rollout.
Before you start¶
You need a cluster you can throw things at and kubectl pointed at it. If you have none, kind is the quickest:
kind create cluster --name pgrr-lab
The lab pulls postgres:16, python:3.12-alpine and the radar's manager image from ghcr.io, so the cluster needs internet access. About 1 CPU and 1.5 GiB of memory are enough.
Step 1: install the radar¶
kubectl apply -f https://raw.githubusercontent.com/joao00001/pg-regression-radar/main/examples/lab/00-radar.yaml
kubectl wait --for=condition=Established --timeout=60s \
crd/postgreswatches.radar.pgregressionradar.io \
crd/deploysources.radar.pgregressionradar.io \
crd/performanceregressions.radar.pgregressionradar.io
kubectl -n pgrr-lab rollout status deployment/pgrr-manager
This creates the namespace, the radar's custom resources (CRDs), its permissions and the radar itself, running in manager mode. The kubectl wait matters: the next step creates objects of the new types, which only works once the CRDs are registered.
Step 2: start the lab¶
kubectl apply -f https://raw.githubusercontent.com/joao00001/pg-regression-radar/main/examples/lab/10-lab.yaml
kubectl -n pgrr-lab wait --for=condition=Available deployment --all --timeout=300s
kubectl -n pgrr-lab get postgreswatch,deploysource
This starts Postgres (seeded with 400,000 orders and an index on orders.order_ref), the orders-api workload, and alert-sink, a small receiver that prints the alerts the radar sends, in place of Slack. It also tells the radar what to watch: a PostgresWatch that scrapes the database every 5 seconds, and a DeploySource that treats every rollout of the orders-api Deployment as a deploy.
Wait about three minutes now. The radar has to see the query running normally before a change means anything. (It also records the first rollout of orders-api as a deploy; waiting lets that one expire so it cannot be mistaken for the bad deploy below.)
In a second terminal, follow the alerts:
kubectl -n pgrr-lab logs -f deployment/alert-sink
Step 3: ship the bad deploy¶
kubectl apply -f https://raw.githubusercontent.com/joao00001/pg-regression-radar/main/examples/lab/20-bad-deploy.yaml
kubectl -n pgrr-lab get performanceregressions -w
Version 2 rolls out, its migration drops the index, and within about a minute a PerformanceRegression appears with status Detected. Look at it:
kubectl -n pgrr-lab describe performanceregressions
The fields to look at in Status: Latency Change Factor (how many times slower), Confidence Score, P Value, and Plan Diff Summary, which names the plan change. In the other terminal, the alert arrives, in the same shape it would have in Slack:
================================================================
:rotating_light: *pg-regression-radar* — performance regression detected on cluster *orders*
Deploy Event k8s-Deployment-pgrr-lab-orders-api-orders-api-2
Query (excerpt) SELECT id, total FROM orders WHERE order_ref = $1
Latency Before 0.05 ms
Latency After 18.00 ms
Change Factor 360.00x
Plan Diff plan diff for queryid ...:
root node type changed: "Index Scan" -> "Seq Scan"
(The numbers depend on your machine; the shape does not.)
Undo and run it again¶
kubectl -n pgrr-lab rollout undo deployment/orders-api
Version 1's migration recreates the index and the query is fast again. To repeat the demo, wait three minutes so the radar sees the healthy baseline again, then re-apply step 3.
Clean up¶
kind delete cluster --name pgrr-lab # if you used kind
# otherwise:
kubectl delete -f https://raw.githubusercontent.com/joao00001/pg-regression-radar/main/examples/lab/00-radar.yaml
If something looks wrong¶
| Symptom | Check |
|---|---|
A pod stays Pending or ImagePullBackOff |
kubectl -n pgrr-lab describe pod <name>: the cluster needs to reach ghcr.io and Docker Hub. |
postgreswatch never reaches Running |
kubectl -n pgrr-lab describe postgreswatch orders and kubectl -n pgrr-lab logs deployment/pgrr-manager. |
| Step 2 fails with "no matches for kind PostgresWatch" | Step 1's kubectl wait did not finish; re-run step 2. |
| No regression after five minutes | You applied step 3 before the baseline was learned, or Postgres was not ready. Check kubectl -n pgrr-lab get pods, undo, wait three minutes and repeat. |
The plan diff says Gather instead of Seq Scan |
Only if you changed the Postgres settings: the lab turns parallel query off so the plan stays readable. |
What is in the manifests¶
| File | What it is |
|---|---|
00-radar.yaml |
Namespace, CRDs, permissions and the manager. Generated from the Helm chart by scripts/build_lab.sh; do not edit by hand. |
10-lab.yaml |
Postgres, orders-api v1, alert-sink, the PostgresWatch and the DeploySource. |
20-bad-deploy.yaml |
orders-api v2: identical to v1 except that its migration drops the index. |
The deploy is detected with sourceType: kubernetes (see Webhooks): the radar watches the Deployment itself, so the lab needs no webhook sender. The settings in the PostgresWatch (5 second scrapes, three samples) are sized for a demo; the defaults are meant for production traffic.
When you are ready for your own database, continue with Installation and Getting Started.