Skip to content

Expose statistics reset timestamps as gauges - #1416

Open
je4yfc wants to merge 4 commits into
prometheus-community:masterfrom
je4yfc:fix/stats-reset-timestamps
Open

je4yfc wants to merge 4 commits into
prometheus-community:masterfrom
je4yfc:fix/stats-reset-timestamps

Conversation

@je4yfc

@je4yfc je4yfc commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

This is my attempt to fix: #1415

Add pg_stat_bgwriter_stats_reset_timestamp_seconds, pg_stat_checkpointer_stats_reset_timestamp_seconds, and pg_stat_database_stats_reset_timestamp_seconds as Unix-second gauges. At master@c5662d1, the background-writer (also before PostgreSQL 17), checkpointer, and database SQL conversion and emission paths turn SQL stats_reset timestamps into Unix seconds but publish counters. Preserve the legacy names, counter types, values and label sets as deprecated aliases, scheduled for removal two releases after deprecation. The new timestamp gauges omit samples when PostgreSQL returns NULL; the legacy counters retain their existing NULL-as-zero behavior. The checkpointer guard stays PostgreSQL 17+, and database labels stay datid,datname. No SQL, flag or legacy-exporter change.

Reproduce the original failure with the focused collector/HTTP exposition test:

git clone https://github.com/prometheus-community/postgres_exporter.git
cd postgres_exporter
git switch --detach c5662d1e61af7fed870049d342215c01263496ba
curl -fsSL https://raw.githubusercontent.com/je4yfc/postgres_exporter/2c68f9c44d1d594b083ea20626d7c29476139fbd/collector/stats_reset_timestamp_test.go -o collector/stats_reset_timestamp_test.go
go test ./collector -run TestStatsResetTimestampExposition -count=1

The original run fails because the gauge families are absent. Regression coverage now lives in the existing collector test files. On this PR branch, go test ./collector -run 'TestPGStat(BGWriter|Checkpointer|Database)Collector' -count=1 passes. Updated HTTP exposition excerpt:

ORIGINAL (c5662d1):
# TYPE pg_stat_bgwriter_stats_reset_total counter
pg_stat_bgwriter_stats_reset_total 1.685059842e+09
# TYPE pg_stat_checkpointer_stats_reset_total counter
pg_stat_checkpointer_stats_reset_total 1.685059842e+09
# TYPE pg_stat_database_stats_reset counter
pg_stat_database_stats_reset{datid="1",datname="first"} 1.685059842e+09
pg_stat_database_stats_reset{datid="2",datname="second"} 0

PATCHED:
# TYPE pg_stat_bgwriter_stats_reset_total counter
pg_stat_bgwriter_stats_reset_total 1.685059842e+09
# TYPE pg_stat_bgwriter_stats_reset_timestamp_seconds gauge
pg_stat_bgwriter_stats_reset_timestamp_seconds 1.685059842e+09
# TYPE pg_stat_checkpointer_stats_reset_total counter
pg_stat_checkpointer_stats_reset_total 1.685059842e+09
# TYPE pg_stat_checkpointer_stats_reset_timestamp_seconds gauge
pg_stat_checkpointer_stats_reset_timestamp_seconds 1.685059842e+09
# TYPE pg_stat_database_stats_reset counter
pg_stat_database_stats_reset{datid="1",datname="first"} 1.685059842e+09
pg_stat_database_stats_reset{datid="2",datname="second"} 0
# TYPE pg_stat_database_stats_reset_timestamp_seconds gauge
pg_stat_database_stats_reset_timestamp_seconds{datid="1",datname="first"} 1.685059842e+09
PASS

Validation: go test ./..., go vet ./..., and git diff --check passed. Tests cover both background-writer version paths, checkpointer's version guard, NULL omission, valid zero timestamps, and metric names, types and labels.

LLM Disclosure: This bug was found and classified by an LLM that I was abusing giving the task of looking through every metric I ingest to see what could be used in a dashboard. After I verified the issue, I had that same LLM help me understand enough of your codebase to make a PR.

@je4yfc
je4yfc force-pushed the fix/stats-reset-timestamps branch from 2c68f9c to 7286f89 Compare October 11, 2026 23:05
Comment thread collector/pg_stat_bgwriter.go Outdated
)
statBGWriterStatsResetTimestampDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, bgWriterSubsystem, "stats_reset_timestamp_seconds"),
"Unix timestamp in seconds at which these statistics were last reset; 0 means PostgreSQL returned NULL",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better not to expose the metric at all if, while scanning the row, we get a NULL value from Postgres. It would definitely be less confusing than seeing a 0 in the dashboard and thinking that the last reset happened in 1970

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -95,7 +95,13 @@ var (
)
statBGWriterStatsResetDesc = prometheus.NewDesc(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a comment stating that this metric will be removed in the next two releases? Mostly for us to remind ourselves to actually remove it 😬

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment marking the counter for removal after two releases

https://github.com/je4yfc/postgres_exporter/blob/6e73ed0/collector/pg_stat_bgwriter.go#L96

@@ -87,7 +87,13 @@ var (
)
statCheckpointerStatsResetDesc = prometheus.NewDesc(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, could we add a comment stating that this metric can be removed in the future?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread collector/pg_stat_checkpointer.go Outdated
)
statCheckpointerStatsResetTimestampDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, statCheckpointerSubsystem, "stats_reset_timestamp_seconds"),
"Unix timestamp in seconds at which these statistics were last reset; 0 means PostgreSQL returned NULL",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, let's not use 0 for NULL returns

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread collector/stats_reset_timestamp_test.go Outdated
@@ -0,0 +1,206 @@
// Copyright 2026 The Prometheus Authors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why create a new file just for this test? couldn't we reuse the existing ones?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ArthurSens ArthurSens Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to be harsh on yourself either, things happen 😅

Comment thread docs/README.md Outdated

All metrics are prefixed with `pg_`. The exporter's own health is exposed via `pg_up` and `pg_exporter_*` metrics alongside the standard Go runtime metrics.

The last statistics-reset time is exposed as a Unix-seconds gauge in `pg_stat_bgwriter_stats_reset_timestamp_seconds`, `pg_stat_checkpointer_stats_reset_timestamp_seconds` (PostgreSQL 17+), and `pg_stat_database_stats_reset_timestamp_seconds` (with `datid` and `datname` labels). A value of `0` means PostgreSQL returned a NULL reset time, so filter for values greater than zero before calculating an age with `time() - metric`. The older `pg_stat_bgwriter_stats_reset_total`, `pg_stat_checkpointer_stats_reset_total`, and `pg_stat_database_stats_reset` families remain as deprecated counters with their existing values for compatibility; they represent timestamps, not reset counts, and should not be used with `rate()`, `increase()`, or `resets()`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't document all our metrics in our README, it would be weird to document this one 😬

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this - sorry haha.

Comment thread CHANGELOG.md Outdated
* [FEATURE] Export `pg_settings.pending_restart` as `pg_settings_pending_restart{name}` from the `settings` collector by @MarcWort in https://github.com/prometheus-community/postgres_exporter/pull/1396
* [FEATURE] Add `aurora_replica_status` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1311

* [ENHANCEMENT] Expose `pg_stat_bgwriter`, `pg_stat_checkpointer`, and `pg_stat_database` statistics reset times as `_timestamp_seconds` gauges; retain the former counter families as deprecated aliases.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changelog entry doesn't follow the format we've been using above. Could you follow it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, since we're deprecating a metric, it would be nice to have a deprecation notice in the changelog as well

@je4yfc je4yfc Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I adeded tis:

https://github.com/je4yfc/postgres_exporter/blob/6e73ed0/CHANGELOG.md#L18

explicit notice naming all three deprecated counters, their replacements, and the two-release removal timeline:

https://github.com/je4yfc/postgres_exporter/blob/6e73ed0/CHANGELOG.md#L19

Signed-off-by: Joseph Erdosy <168128519+je4yfc@users.noreply.github.com>
Signed-off-by: Joseph Erdosy <168128519+je4yfc@users.noreply.github.com>
@je4yfc
je4yfc force-pushed the fix/stats-reset-timestamps branch from 6e73ed0 to 08f755e Compare October 29, 2026 16:26
Signed-off-by: Joseph <168128519+je4yfc@users.noreply.github.com>
@je4yfc

je4yfc commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

I think I fixed it all, I'll leave resolution to you. If you need additional fixed, let me know 🫡

@ArthurSens ArthurSens left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just some nits

Comment thread CHANGELOG.md Outdated
* [FEATURE] Export `pg_settings.pending_restart` as `pg_settings_pending_restart{name}` from the `settings` collector by @MarcWort in https://github.com/prometheus-community/postgres_exporter/pull/1396
* [FEATURE] Add `aurora_replica_status` collector for Amazon Aurora PostgreSQL by @megative in https://github.com/prometheus-community/postgres_exporter/pull/1311
* [BUGFIX] Expose `pg_stat_bgwriter`, `pg_stat_checkpointer`, and `pg_stat_database` statistics reset times as `_timestamp_seconds` gauges, omitting samples when PostgreSQL returns NULL, by @je4yfc in https://github.com/prometheus-community/postgres_exporter/pull/1416
* [CHANGE] Deprecate `pg_stat_bgwriter_stats_reset_total`, `pg_stat_checkpointer_stats_reset_total`, and `pg_stat_database_stats_reset` in favor of their `_stats_reset_timestamp_seconds` gauges; the counters will be removed two releases after this deprecation, by @je4yfc in https://github.com/prometheus-community/postgres_exporter/pull/1416

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to be explicit on what "two releases from now" means. Which is after v0.22.0

Comment thread collector/pg_stat_database.go Outdated
[]string{"datid", "datname"},
prometheus.Labels{},
)
// Deprecated: remove this counter two releases after introducing the timestamp gauge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's be explicit about which version we're removing the metric

Comment thread collector/pg_stat_bgwriter.go Outdated
[]string{},
prometheus.Labels{},
)
// Deprecated: remove this counter two releases after introducing the timestamp gauge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

@je4yfc

je4yfc commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

Fixed. I set them to 0.22.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Statistics reset timestamps are exposed as counters in three collectors

2 participants