Skip to content

Conversation

@korlenko
Copy link

@korlenko korlenko commented Jan 5, 2026

…ge()

Description
This PR fixes an incorrect calculation in the postgres_xacts_left_before_wraparound metric related to MultiXact IDs. Currently, the query uses the age() function for values derived from datminmxid. However, according to the PostgreSQL documentation, age() is intended for transaction IDs (XID) and must not be used for MultiXact IDs (MXID).

PostgreSQL provides a dedicated function for this purpose: "mxid_age (xid) → integer
Returns the number of multixact IDs between the supplied multixact ID and the current multixacts counter." — PostgreSQL Documentation - https://www.postgresql.org/docs/14/functions-info.html

Using age(datminmxid) results in incorrect values because it implicitly treats a MultiXact ID as a transaction ID, which leads to inflated ages and false wraparound alerts.

This PR replaces age() with mxid_age() for the MultiXact-related part of the metric, which aligns the calculation with PostgreSQL documentation and eliminates false positives.

…ge()

Description
This PR fixes an incorrect calculation in the postgres_xacts_left_before_wraparound metric related to MultiXact IDs.
Currently, the query uses the age() function for values derived from datminmxid. However, according to the PostgreSQL documentation, age() is intended for transaction IDs (XID) and must not be used for MultiXact IDs (MXID).

PostgreSQL provides a dedicated function for this purpose:
"mxid_age (xid) → integer
Returns the number of multixact IDs between the supplied multixact ID and the current multixacts counter."
— PostgreSQL Documentation - https://www.postgresql.org/docs/14/functions-info.html

Using age(datminmxid) results in incorrect values because it implicitly treats a MultiXact ID as a transaction ID, which leads to inflated ages and false wraparound alerts.

This PR replaces age() with mxid_age() for the MultiXact-related part of the metric, which aligns the calculation with PostgreSQL documentation and eliminates false positives.
@cherts
Copy link
Owner

cherts commented Jan 12, 2026

Hi, @korlenko

your fix and comment is correct, thanks, but the query still seems to have an small error

In the expression
MAX(MXID_AGE(COALESCE(NULLIF(datminmxid, 1), datfrozenxid))))
for most databases NULLIF(datminmxid, 1) will return NULL, and COALESCE will take the result of the datfrozenxid field, making the use of MXID_AGE incorrect.

I think this expression should be
COALESCE(MAX(MXID_AGE(NULLIF(datminmxid, 1))), MAX(AGE(datfrozenxid)))

@korlenko
Copy link
Author

Hi, @cherts
Yes, you’re right. I tested a reduced version of the query and forgot to move "datfrozenxid" out from under MXID in the second part.
I’ve fixed it. Thanks!

@cherts cherts merged commit 0e1dc0d into cherts:release/0.15 Jan 13, 2026
1 check passed
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.

2 participants