Query icon
If icon
SlackIncomingWebhook icon
Log icon
Schedule icon
Webhook icon

Audit PostgreSQL Slow Queries and Missing Indexes with Kestra

Audit PostgreSQL with Kestra - slow queries from pg_stat_statements, seq-scan index candidates, one Slack advisory, weekly or on demand.

Categories
Data

PostgreSQL records every statement in pg_stat_statements, but the view only answers questions someone remembers to ask. Nobody spot-checks it weekly, the report never reaches the team channel, and the table that has been doing sequential scans for months keeps doing them. This blueprint turns that view into a standing advisory: the queries that deserve attention, the tables that probably deserve an index, delivered where the team already reads.

How it works

  1. fetch_slow_queries (io.kestra.plugin.jdbc.postgresql.Query, fetchType: FETCH) reads pg_stat_statements for statements whose mean time is at least min_mean_ms and that ran at least min_calls times, ordered by cumulative time, with newlines flattened out of the SQL text and capped at top_n rows.
  2. fetch_totals (same plugin, FETCH_ONE) aggregates the same filters into a count and a cumulative-time figure so the Slack message can state totals without parsing rows in Pebble.
  3. fetch_index_candidates reads pg_stat_user_tables for tables with at least min_table_rows estimated rows where sequential scans outnumber index scans - the classic shape of a query that should have been an index scan.
  4. audit_gate (io.kestra.plugin.core.flow.If) posts the advisory to Slack when either list is non-empty, and logs a quiet all-clear otherwise. The alert lists each slow query with its call count, mean and total time, and each candidate table with its scan counts.
  5. The flow-level errors handler posts a distinct Slack message when the audit itself fails, so a broken connection or a missing extension is loud instead of silently missing a week.

The thresholds are deliberately inputs: a 20 ms floor suits an OLTP fleet, a 500 ms floor suits an analytics warehouse, and min_calls keeps a single exploratory statement from dominating the report.

What you get

  • A ranked list of the statements actually costing time, with numbers, in the team channel.
  • Index candidates derived from real scan statistics, not guesswork.
  • Counts and cumulative time as flow outputs, usable by downstream tasks or dashboards.
  • A weekly cadence plus an on-demand webhook for after-migration checks.

Who it's for

  • DBAs and backend teams who own a PostgreSQL instance and want a weekly performance pulse.
  • Platform teams running managed Postgres (RDS, Cloud SQL, Aurora) without Enterprise query tools.
  • Engineers who just shipped a migration and want immediate evidence of what changed.

Why orchestrate this with Kestra

A pg_stat_statements query in a cron job is a script: no retries when the connection blips, no execution history when someone asks what the report said three weeks ago, no built-in alert when the query itself breaks. Kestra gives the audit scheduled and event triggers, retries, execution lineage, inputs you can tune without editing SQL, and a failure path that pages instead of vanishing into a cron log.

Prerequisites

  • PostgreSQL 13 or newer (the flow uses the mean_exec_time / total_exec_time columns).
  • The pg_stat_statements extension installed and loaded via shared_preload_libraries (managed services usually expose it as a parameter).
  • A Kestra instance with network access to the database.

Secrets

  • POSTGRES_JDBC_URL: JDBC URL for the database (for example jdbc:postgresql://host:5432/postgres).
  • POSTGRES_USERNAME: database username.
  • POSTGRES_PASSWORD: database password.
  • SLACK_WEBHOOK_URL: Slack incoming webhook URL for the advisory and failure alerts.

Quick start

  1. Add the four secrets above to your Kestra instance.
  2. Confirm the extension answers: SELECT count(*) FROM pg_stat_statements;.
  3. Run the flow once manually and check the fetch_slow_queries output rows.
  4. Trigger it from the on_demand webhook with your own key, then enable the weekly schedule.

How to extend

  • Raise min_mean_ms and min_calls to keep the channel quiet, or lower them after a migration to catch small regressions.
  • Add a task after audit_gate that opens a GitHub issue from the same rows, so the advisory leaves a permanent backlog item.
  • Chain this flow after a deployment with a Flow trigger and compare the outputs between releases.
  • Feed slowest_query into an EXPLAIN automation that plans the statement against production statistics.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.