Skip to content

feat(database_observability.postgres): Add index-usage collectors - #7068

Merged
gaantunes merged 12 commits into
mainfrom
feat/dbo11y-postgres-index-usage-collectors
Sep 14, 2026
Merged

gaantunes merged 12 commits into
mainfrom
feat/dbo11y-postgres-index-usage-collectors

Conversation

@gaantunes

Copy link
Copy Markdown
Contributor

Brief description of Pull Request

Split out of #7003 (originally combined postgres + mysql; splitting per one-logical-change-per-PR).

Adds two opt-in collectors to database_observability.postgres, disabled by default and enabled via enable_collectors, emitting only the metrics needed for missing-index and unused-index KG insights:

  • table_statspg_stat_user_tables_seq_scan/idx_scan/n_live_tup.
  • index_statspg_stat_user_indexes_idx_scan_total, pg_index_properties{is_primary}, pg_index_size_bytes.

Both fan out across every database the connection can reach, reusing the discovery/connection mechanism already used by schema_details (extracted into a shared multi_database.go). This fixes the vendored postgres_exporter's single-DSN limitation for these system views natively, rather than waiting on the unmerged upstream prometheus-community/postgres_exporter#1378.

Metric names/labels match the existing pg_stat_user_tables_* fields, and, for the new index metrics, the shape proposed by the unmerged upstream prometheus-community/postgres_exporter#1071.

Also fixes a redundant-connection issue in the multi-database fan-out (connectToDatabase): it always opened a fresh connection via factory(), even for the one database that's already the database the initial connection points to, since a freshly-sql.Open'd *sql.DB is never pointer-equal to the initial one. It now detects that case up front and reuses the initial connection directly instead.

Validated against a live db-o11y-postgres-16 test instance hosting real books_store data: every emitted series across both collectors matched the database source exactly.

Pull Request Details

Issue(s) fixed by this Pull Request

Notes to the Reviewer

PR Checklist

  • Documentation added
  • Tests updated
  • Config converters updated
  • This pull request was substantially generated with AI assistance (see the GenAI policy)

…dex detection

Adds two opt-in collectors, disabled by default and enabled via
enable_collectors: table_stats emits pg_stat_user_tables_seq_scan/idx_scan/
n_live_tup; index_stats emits pg_stat_user_indexes_idx_scan_total,
pg_index_properties{is_primary}, and pg_index_size_bytes. Both fan out
across every database the connection can reach (reusing schema_details'
discovery/connection mechanism, extracted into a shared multi_database.go),
fixing the vendored postgres_exporter's single-DSN limitation for these
system views natively rather than waiting on the unmerged upstream
prometheus-community/postgres_exporter#1378.

Metric names/labels match the existing pg_stat_user_tables_* fields, and,
for the new index metrics, the shape proposed by the unmerged upstream
prometheus-community/postgres_exporter#1071.

Also fixes a redundant-connection issue in the multi-database fan-out
(connectToDatabase): it always opened a fresh connection via factory(),
even for the one database that's already the database the initial
connection points to, since a freshly-sql.Open'd *sql.DB is never
pointer-equal to the initial one. It now detects that case up front and
reuses the initial connection directly.

Validated against a live db-o11y-postgres-16 test instance hosting real
books_store data: every emitted series across both collectors matched the
database source exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

💻 Deploy preview deleted (feat(database_observability.postgres): Add index-usage collectors).

@gaantunes
gaantunes marked this pull request as ready for review September 9, 2026 19:35
@gaantunes
gaantunes requested review from a team and clayton-cornell as code owners September 9, 2026 19:35
gaantunes and others added 4 commits September 10, 2026 11:56
Drop the source system view names (pg_stat_user_tables/pg_stat_user_indexes)
from the collector table, matching the terse, purpose-focused style already
used for the other rows in this table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Shorten doc comments in the new postgres collectors: drop "KG insight"
task-framing and restate-the-code prose, keep the genuinely non-obvious
rationale (multi-database scoping, the redundant-connection gotcha,
upstream-name matching) but state it more directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Renames pg_stat_user_tables_*, pg_stat_user_indexes_idx_scan_total,
pg_index_properties, and pg_index_size_bytes to
database_observability_pg_stat_user_tables_*,
database_observability_pg_stat_user_indexes_idx_scan_total,
database_observability_pg_index_properties, and
database_observability_pg_index_size_bytes -- matching the namespace
ConnectionInfo and the logs collector's error metrics already use.

This also closes a latent registry collision: table_stats' prior names were
identical to the real embedded postgres_exporter's own stat_user_tables
collector output, so enabling both table_stats and the prometheus_exporter
block for the same target would have hit the same "duplicate metrics
collector registration" conflict fixed for mysql's table_stats/index_stats
a few commits back.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@clayton-cornell clayton-cornell added the type/docs Docs Squad label across all Grafana Labs repos label Sep 10, 2026
@clayton-cornell

Copy link
Copy Markdown
Contributor

All looks good for doc changes :-)

Comment thread internal/component/database_observability/postgres/collector/multi_database.go Outdated
Comment thread internal/component/database_observability/postgres/collector/index_stats.go Outdated
Comment thread internal/component/database_observability/postgres/collector/index_stats.go Outdated
Comment thread internal/component/database_observability/postgres/collector/index_stats.go Outdated
gaantunes and others added 2 commits September 11, 2026 13:34
…dex_properties

Per Cristian's review on the postgres PR:
- Move discoverDatabases/connectToDatabase and selectAllDatabases into
  dsn.go, since DSN parsing and per-database connection fan-out go
  together; drop the now-empty multi_database.go/_test.go.
- Drop the pg_index_properties metric and its is_primary column/join:
  schema_details already surfaces primary-key info, and index_stats has
  no equivalent in the mysql PR. This also removes the double-append
  needed only to build that metric's label list.
- Trim the now-stale "field names match upstream" comment on index_stats.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rather than a separate constant-1 index_properties metric, attach
is_primary, is_unique, and is_partial directly as labels on
index_size_bytes. These are 1:1 with an index the same way schemaname
and indexrelname already are, so this adds no series -- a separate
info-style metric would have cost a full extra series per index for
re-emitting the same identifying labels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
gaantunes and others added 4 commits September 11, 2026 14:29
Mirrors the mysql_index_stats/mysql_table_stats subsystem naming from
the mysql PR: pg_stat_user_indexes/pg -> pg_index_stats (idx_scan_total,
size_bytes), pg_stat_user_tables -> pg_table_stats (seq_scan, idx_scan,
n_live_tup). SQL/log references to the actual pg_stat_user_indexes and
pg_stat_user_tables views are unchanged -- only the metric namespace
segment moves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
seq_scan and idx_scan are registered as CounterValue (cumulative since
the last stats reset), so per Prometheus convention they need a _total
suffix, matching pg_index_stats_idx_scan_total. n_live_tup stays as-is
since it's a GaugeValue, not cumulative.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
row_count is the cross-engine name settled on for this gauge, so mysql's
upcoming equivalent (mysql.innodb_table_stats.n_rows) can share it. Not
row_total: this is a live snapshot that can go up or down, not a
monotonic counter, so a _total suffix would be misleading.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"the primary key" instead of "a primary key" -- a table has exactly
one, in either engine. Found while auditing all new metrics across
both PRs for cross-engine consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
gaantunes added a commit that referenced this pull request Sep 11, 2026
…ls and a new row_count metric (#7095)

### Brief description of Pull Request

Adds two signals to the `index_stats`/`table_stats` collectors,
mirroring the same additions made to the postgres collectors in #7068:

- `is_primary`/`is_unique` labels on `mysql_index_stats_size_bytes`.
`is_primary` is derived from `INDEX_NAME = 'PRIMARY'` (no extra query
needed); `is_unique` is joined in from
`information_schema.statistics.NON_UNIQUE`.
- `mysql_table_stats_row_count`, a new gauge sourced from
`mysql.innodb_table_stats.n_rows` (the sibling table to
`mysql.innodb_index_stats`, already used for index size).

### Pull Request Details

Both are needed for the missing/unused-index Knowledge Graph insight:

- Without `is_primary`/`is_unique`, the unused-index insight has no
signal to avoid recommending the removal of an index that's actually
backing a primary key or unique constraint just because its usage
counters look low. These are folded onto the existing `size_bytes` gauge
as labels rather than a separate metric, since both attributes are 1:1
with an index the same way `schema`/`table`/`index` already are — this
adds no additional series.
- Without `row_count`, the missing-index insight can't tell a table
that's genuinely being hammered with full scans from one that's just
small enough that a full scan doesn't matter — the same gap closed on
the postgres side by `pg_table_stats_row_count`.

### Issue(s) fixed by this Pull Request



### Notes to the Reviewer



### PR Checklist

- [ ] Documentation added
- [ ] Tests updated
- [ ] Config converters updated
- [ ] This pull request was substantially generated with AI assistance
(see the [GenAI
policy](https://github.com/grafana/alloy/blob/main/docs/developer/genai.md))

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@gaantunes
gaantunes merged commit e9c7d65 into main Sep 14, 2026
60 checks passed
@gaantunes
gaantunes deleted the feat/dbo11y-postgres-index-usage-collectors branch September 14, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/docs Docs Squad label across all Grafana Labs repos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants